• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Описание регистров ESP8266 в стиле STM

Garmin

Member
Только начинаю писать свои программы для ESP8266. Установил UDK, компилирую учебные проекты.
При попытке разобраться с имеющимися примерами я был просто в шоке от стиля написания программ. Про форматирование текста я не говорю, но сумасшедшие макросы и дефайны делают программу нечитаемой и только условно работающей. Например, макрос
[inline]PIN_FUNC_SELECT(PIN_NAME, FUNC)[/inline]
вообще не компилируется без предупреждений, пришлось заменить его инлайн функцией.
Но я хочу поговорить о другом: о доступе к регистрам. Применяемые макросы типа
[inline] WRITE_PERI_REG (SPI_USER2 (HSPI), regvalue);[/inline]
или
[inline]SET_PERI_REG_MASK (SPI_CMD (HSPI), SPI_USR);//transmission start[/inline]
без комментариев совершенно непонятны.
Я написал пример описания регистров SPI в том стиле, к которому привык - как описаны регистры в процессорах STM. При этом регистры описаны дважды: в виде битовых полей и в виде битовых масок. Попробовал скомпилировать и в виде битовых масок, и в виде битовых полей - прошло без ошибок.
Пример старого и нового подхода:
Код:
    pin_func_select (PERIPHS_IO_MUX_MTDI_U, 2);    //configure io12 to hspi_miso mode
    pin_func_select (PERIPHS_IO_MUX_MTCK_U, 2);//configure io13 to Hspi_mosi mode
    pin_func_select (PERIPHS_IO_MUX_MTMS_U, 2);//configure io14 to Hspi_clk mode
    pin_func_select (PERIPHS_IO_MUX_MTDO_U, 2);//configure io15 to Hspi_cs mode
// использование регистров
    SPI1->spi_user |= SPI_USER_CS_SETUP | SPI_USER_CS_HOLD | SPI_USER_COMMAND;
    SPI1->spi_user &= ~SPI_USER_FLASH_MODE;
    SPI1->spi_clock_bits.equ_sysclk = 0;
    SPI1->spi_clock_bits.pre_s = SPI_PREDIV_CLK;
    SPI1->spi_clock_bits.clkcnt_n = SPI_DIV_CLK_N;
    SPI1->spi_clock_bits.clkcnt_h = SPI_DIV_CLK_H;
    SPI1->spi_clock_bits.clkcnt_l = SPI_DIV_CLK_L;
// использование макросов
    SET_PERI_REG_MASK (SPI_USER (HSPI),
            SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_COMMAND);
    CLEAR_PERI_REG_MASK (SPI_USER (HSPI), SPI_FLASH_MODE);
    WRITE_PERI_REG (SPI_CLOCK (HSPI),
        ((SPI_PREDIV_CLK & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S)
            | ((SPI_DIV_CLK_N & SPI_CLKCNT_N) << SPI_CLKCNT_N_S)
            | ((SPI_DIV_CLK_H & SPI_CLKCNT_H) << SPI_CLKCNT_H_S)
            | ((SPI_DIV_CLK_L & SPI_CLKCNT_L) << SPI_CLKCNT_L_S));//clear bit 31, set SPI clock div
У меня не хватает опыта работы в Eclipse, чтобы сравнить ассемблерный выход между старыми макросами и новыми выражениями. Проект с файлами я выложил на гитхаб.
Если кому это интересно, посмотрите файлы spi.c, spi.h и eagle_soc.h и проверьте, правильно ли компилятор обрабатывает новые определения.
 

vad7

Active member
Ассемблерный дамп:
@if exist .\app\.output\eagle\image\eagle.app.v6.out C:\Espressif\xtensa-lx106-elf\bin\xtensa-lx106-elf-objdump -S .\app\.output\eagle\image\eagle.app.v6.out > eagle.app.v6.asm

В makefile флаг -g должен быть:
CCFLAGS += -g \
 

Garmin

Member
Я в makefile пока не силён. До этого работал в IAR под виндой. Так что показывайте на пальцах, пожалуйста. С этим eagle.app.v6 я мучаюсь при компиляции других проектов из папки examples.
 

NutsXXXL

New member
@Garmin дело правильное, благородное.. во всей этой esp кавардак на костыле и глюком погоняет. какой уж тут порядок.. но люди работают.
однако в целом makefile + GCC это определенный стандарт, expressif сделали очень заумный makefile
нов целом это выглядит так надо найти makefile где есть строчка определение CCFLAGS (в принципе он находиться в корне репозиттория "Root Level Makefile")
и добавить в это определение ключ -g

@vad7
это ведь включить генерирования .asm которые можно будет сравнивать?
 

Garmin

Member
Флаг -g в makefile есть. Строку с генерацией ассемблера я не знаю, куда вставить. Возле цели :all она вызывает ошибку. Я создал батник и вызвал утилиту вручную. Получил портянку. Вот листинг функции spi_master_init:

Кто- нибудь сможет сравнить результаты работы компилятора? Я в этом ассемблере разбираюсь чуть менее, чем никак.
 

Вложения

vad7

Active member
Строку генерации асма можно вообще вставить в батник и запускать отдельно. Ну и пути подредактировать под свои.
-g - генерить отладочную информацию, без него строчки С исходника не будут в асм файл впечатываться.
 

NutsXXXL

New member
@Garmin кстати да проблема то в том что вообще мало людей в мире разбираются в синтаксисе конкретно этого ассемблера..
 

Garmin

Member
Я думаю, нужно попробовать описать таким образом GPIO или UART и попробовать заменить часть макросов в каком-нибудь рабочем проекте. Если всё будет работать аналогичным образом, значит описание правильное, его можно использовать.
 

Garmin

Member
Сегодня сделал описание регистров UART в этом же стиле. Файл "uart_register.h" выложил в том же репозитории.
Пример использования:
Код:
/******************************************************************************
* FunctionName : uart_config
* Description  : Internal used function
*                UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled
*                UART1 just used for debug output
* Parameters   : uart_no, use UART0 or UART1 defined ahead
* Returns      : NONE
*******************************************************************************/
LOCAL void ICACHE_FLASH_ATTR
uart_config(uint8 uart_no)
{
    UART_TypeDef *p_uart;
    if (uart_no == UART1)
    {
        p_uart = (UART_TypeDef *)UART1_BaseAddress;
        pin_func_select (PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
    }
    else
    {
         p_uart = (UART_TypeDef *)UART0_BaseAddress;
       /* rcv_buff size if 0x100 */
        ETS_UART_INTR_ATTACH(uart0_rx_intr_handler,  &(UartDev.rcv_buff));
        PIN_PULLUP_DIS (PERIPHS_IO_MUX_U0TXD_U);
        pin_func_select (PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
        pin_func_select (PERIPHS_IO_MUX_MTDO_U, FUNC_U0RTS);
        pin_func_select (PERIPHS_IO_MUX_MTCK_U, FUNC_U0CTS);
        PIN_PULLUP_DIS (PERIPHS_IO_MUX_MTCK_U);
    }

    uart_div_modify(uart_no, (uint16)(UART_CLK_FREQ / (uint32)(UartDev.baut_rate)));

    p_uart->uart_conf0 = (uint32)UartDev.exist_parity | (uint32)UartDev.parity
            | (((uint8)UartDev.stop_bits) << UART_CONF0_STOP_BIT_NUM_S)
            | (((uint8)UartDev.data_bits) << UART_CONF0_BIT_NUM_S);

/*    WRITE_PERI_REG (UART_CONF0 (uart_no), (uint32)UartDev.exist_parity
            | (uint32)UartDev.parity
            | (((uint8)UartDev.stop_bits) << UART_STOP_BIT_NUM_S)
            | (((uint8)UartDev.data_bits) << UART_BIT_NUM_S));
*/
    //clear rx and tx fifo,not ready
    SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
    CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);

    if (uart_no == UART0) {
        //set rx fifo trigger
        WRITE_PERI_REG(UART_CONF1(uart_no),
                ((100 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) |
                ((110 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) |
                UART_RX_FLOW_EN |
                (0x02 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S |
                UART_RX_TOUT_EN |

                ((0x10 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S)); //wjl
                //SET_PERI_REG_MASK( UART_CONF0(uart_no),UART_TX_FLOW_EN);  //add this sentense to add a tx flow control via MTCK( CTS )

        SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_TOUT_INT_ENA |
                UART_FRM_ERR_INT_ENA);
    } else {
        WRITE_PERI_REG(UART_CONF1(uart_no),
                ((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S));
    }

    //clear all interrupt
    WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff);
    //enable rx_interrupt
    SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_OVF_INT_ENA);
}
У меня есть две просьбы:
1) проверить работу ESP8266 при использовании нового описания
2) добавить комментариев к битовым маскам и регистрам из экселевского файла "uart_reg.xls", у меня не хватает времени на описание регистров.
 

Garmin

Member
Добавил описания GPIO и IOMUX. Также нашёл дополнительные регистры SPI_EXT, но их описания не нашёл.
Прошло несколько дней, но ответа или проверки я не дождался. Наверное, эта тема не интересна.
Хотя я не могу понять, неужели всех, кто пишет программы для ESP, устраивает этот бардак с регистрами, а особенно эти ужасные макросы?
Ведь макросы нельзя отлаживать в принципе, да и программа становится понятнее и короче.
Например, такая простыня:
Код:
pin_reg = GPIO_REG_READ(GPIO_PIN_ADDR(io_num));
pin_reg &= (~GPIO_PIN_DRIVER_MASK);
pin_reg |= (GPIO_PAD_DRIVER_ENABLE << GPIO_PIN_DRIVER_LSB);
GPIO_REG_WRITE(GPIO_PIN_ADDR(io_num), pin_reg);
лёгким движением руки превращается в строчки:
Код:
  __IO GPIO_pin_t *p_pin = &GPIO->pin_0_bits + sizeof (GPIO_pin_t) * (config->num_pin);
p_pin->pin_driver = OPEN_DRAIN;
 

Garmin

Member
Итак, через месяц после первого знакомства с ESP8266, я уже умею компилировать свои прошивки. Для теста файлов описания взял проект at_v0.20_on_SDKv0.9.4.
После некоторых плясок с бубном и исправления выявленных косяков в описаниях iomux_registers.h и uart_registers.h
программа инициализации уарта теперь выглядит вот так:
Код:
static void ICACHE_FLASH_ATTR uart_config (uint8 uart_no)
{
    UART_TypeDef *uart_p;
  
    if (uart_no == UART1)
    {
        uart_p = UART_1;
        IOMUX->gpio2_mux &= ~GPIO_MUX_FUNC_MASK;
        IOMUX->gpio2_mux |= GPIO2_FUNC_U1TXD;
    }
    else
    {
        uart_p = UART_0;
        /* rcv_buff size if 0x100 */
        ets_isr_attach (5, uart0_rx_intr_handler, (void *)&UartDev.rcv_buff);
        IOMUX->gpio1_mux_bits.pullup = 0;  

        IOMUX->gpio1_mux_u0_txd &= ~GPIO_MUX_FUNC_MASK;
        IOMUX->gpio1_mux_u0_txd |= GPIO1_FUNC_U0TXD;

        IOMUX->gpio15_mux_mtdo &= ~GPIO_MUX_FUNC_MASK;
        IOMUX->gpio15_mux_mtdo |= GPIO15_FUNC_U0RTS;
    }
  
    uart_div_modify (uart_no, UART_CLK_FREQ / (UartDev.baut_rate));
  
    uart_p->uart_conf0 = (UartDev.exist_parity
            | UartDev.parity
            | (UartDev.stop_bits << UART_STOP_BIT_NUM_S)
            | (UartDev.data_bits << UART_BIT_NUM_S));
  
    //clear rx and tx fifo,not ready
    uart_p->uart_conf0 |= UART_CONF0_RXFIFO_RST | UART_CONF0_TXFIFO_RST;  
    uart_p->uart_conf0 &= ~(UART_CONF0_RXFIFO_RST | UART_CONF0_TXFIFO_RST);  
  
    if (uart_no == UART0)
    {
        //set rx fifo trigger
        uart_p->uart_conf1_bits.rx_fifo_full_trhd = 0x10;
        uart_p->uart_conf1_bits.rx_flow_trhd = 0x10;
        uart_p->uart_conf1_bits.rx_flow_en = 1;
        uart_p->uart_conf1_bits.rx_tout_trhd = 0x02;
        uart_p->uart_conf1_bits.rx_tout_en = 1;
      
        uart_p->uart_int_ena |= UART_INT_ENA_RXFIFO_TOUT | UART_INT_ENA_FRM_ERR;
    }
    else
    {
        uart_p->uart_conf1_bits.rx_fifo_full_trhd = UartDev.rcv_buff.TrigLvl;
    }
  
    //clear all interrupt
    uart_p->uart_int_clr = UART_INT_CLR_MASK;
    //enable rx_interrupt
    uart_p->uart_int_ena_bits.rx_fifo_full = 1;
}
Этот код работает! Для меня теперь гораздо понятнее логика работы программы.
Я пока не коммитил изменения на гитхаб, нужно проверять дальше.
 
Последнее редактирование:

Garmin

Member
Прошёл определённый путь.
Созданы описания большей части внутренней периферии. Последнее, что было упорядочено - SAR. Только информации по нему совсем мало.
Переписаны примеры работы UART и SPI без макросов, с новыми определениями, получается ясно и понятно. Примеры в папках ILI9341 и SPI_slave страшно кривые :)

Но я о другом - хочу поделиться успехом.
Поднял обмен между внешним процессором STM32 и ESP8266 по SPI:
- Обмен полудуплексный по трём проводам плюс запрос (четвёртый провод)
- обмен данными, блоками по 64 байта. полностью аппаратный
- ESP8266 работает в slave режиме (обусловлено ограничениями STM32)
- скорость задаётся STM32, сейчас 10 МГц
- Передаются только данные, без команд и адресов.
- Разруливаются конфликты при передаче данных.

Сейчас буду проверять работу с потоком данных 2 МБит в эхо-режиме.
В планах этот поток засунуть в UDP... :)
 

pvvx

Активный участник сообщества
Также нашёл дополнительные регистры SPI_EXT, но их описания не нашёл.
spi_overlap.c:
Код:
    //set higher priority for spi than hspi
    SET_PERI_REG_MASK(SPI_EXT3(SPI),0x1);
    SET_PERI_REG_MASK(SPI_EXT3(HSPI),0x3);
    SET_PERI_REG_MASK(SPI_USER(HSPI), BIT(5));  // (BIT(5)) = SPI_CS_SETUP   +1 такт перед CS
Остальное ясно из названия и значений по умолчанию в регистрах:
Код:
#define SPI_FLASH_EXT0(i)            (REG_SPI_BASE(i)  + 0xF0) // 800a0050/800a0050
#define SPI_T_PP_ENA                    (BIT(31))
#define SPI_T_PP_SHIFT                    0x0000000F
#define SPI_T_PP_SHIFT_S                16
#define SPI_T_PP_TIME                    0x00000FFF
#define SPI_T_PP_TIME_S                    0

#define SPI_FLASH_EXT1(i)            (REG_SPI_BASE(i) + 0xF4) // 800f0258/800f0258
#define SPI_T_ERASE_ENA                    (BIT(31))
#define SPI_T_ERASE_SHIFT                0x0000000F
#define SPI_T_ERASE_SHIFT_S                16
#define SPI_T_ERASE_TIME                0x00000FFF
#define SPI_T_ERASE_TIME_S                0

#define SPI_FLASH_EXT2(i)            (REG_SPI_BASE(i) + 0xF8) //00000000/00000000 
#define SPI_ST                             0x00000007
#define SPI_ST_S                        0
 

pvvx

Активный участник сообщества
Поднял обмен между внешним процессором STM32 и ESP8266 по SPI:
Не описали тип протокола - SDIO.
ROM-BIOS уже сама позволяет вести работу через SPI с WiFi... Так было задумано, код находиться в ROM, но работоспособность не проверялась и спецификации от Espressif не дано.
Смотрите режимы "загрузки":
SDIO HighSpeed V2 IO
SDIO LowSpeed V1 IO
SDIO HighSpeed V1 IO
SDIO LowSpeed V2 IO
В них включается SDIO с DMA (SLC) и идет работа по SDIO...
https://www.sdcard.org/developers/overview/sdio/sdio_spec/Simplified_SDIO_Card_Spec.pdf
 
Последнее редактирование:

Garmin

Member
spi_overlap.c:
Остальное ясно из названия и значений по умолчанию в регистрах:
Благодарю за внимание, но spi_overlap, как и некоторые другие файлы, я изучал, и эти определения включены в структуру. Точно также, как и IOMUX, DPORT, SLC, SAR.
Пока я проверяю описания, и не коммитил изменения в гитхаб.
Однако описать биты это не значит уметь ими пользоваться.
Я хотел бы использовать SLC(DMA), но пока с ним не разобрался.

По поводу описаний SDIO. Насколько я понимаю, это не физический интерфейс, а программный, также как и I2C. Просто используется определённый режим SPI. Так что регистры SPI нет нужды описывать как регистры SDIO. Поправьте меня, если я ошибаюсь.
 

pvvx

Активный участник сообщества
По поводу описаний SDIO. Насколько я понимаю, это не физический интерфейс, а программный, также как и I2C. Просто используется определённый режим SPI. Так что регистры SPI нет нужды описывать как регистры SDIO. Поправьте меня, если я ошибаюсь.
Это полу-программный интерфейс. Контроллер SPI интегрирован и с сиcтемой "кеширования" flash для CPU, работая полностью аппаратно. При работе с flash так-же используется часть этого аппаратного комплекса. Как пример - при работе контроллера SPI в ESP в коде не написано задание номеров команд чтения или записи flash, а производиться взводом бита в регистре управления и далее всё происходит автоматически. Аналогично и с SDIO. Для доп.управления этим интерфейсом выделена дополнительная область регистров, но не опубликована Espressif.
Предположительно это область [inline]extern volatile uint32 sdio_[64]; // 0x60000A00[/inline]
Скорее всего это опять связано с нарушением патентов...
 

pvvx

Активный участник сообщества
Я хотел бы использовать SLC(DMA), но пока с ним не разобрался.
Пока нет описания SLC. Отсутствуют указания целей и стыковки с разными интерфейсами (биты запросов и подтверждений). Есть только жесткие примеры, которые невозможно переконфигурировать под свои задачи.
 

Garmin

Member
Ничего смертельного. Будем работать ручками, не впервой.
Кстати, чтобы не тратить ещё время - где в SDK программы остановки и запуска WiFi модуля, только не биты останова тактирования в регистрах?
Мне нужно сначала сконфигурировать MAC, mode, TCP, а потом начинать трансляцию WiFi и подключение модулей друг к другу.
 

Garmin

Member
Обновил файлы на гитхабе. Описания регистров доступны здесь.
Все файлы с множественными названиями (типа gpio_registers.h) - новые. Пользуйтесь на здоровье, кому интересно.
 
Сверху Снизу