• Система автоматизации с открытым исходным кодом на базе 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) - новые. Пользуйтесь на здоровье, кому интересно.
 
Сверху Снизу