• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

не работают потоковые операции spi0

sharikov

Active member
spi_master_write работает а все потоковые как interrupt так dma - нет.

Код:
// SPI0 (S0)
#define SPI0_MOSI  PC_2
#define SPI0_MISO  PC_3
#define SPI0_SCLK  PC_1
#define SPI0_CS    PC_0

volatile uint8_t MasterRxDone, MasterTxDone;
spi_t spi_master;

void master_tr_done_callback(void *pdata, SpiIrq event)
{
    switch(event){
    case SpiRxIrq:
        //DBG_8195A("Master RX done!\n");
        MasterRxDone = 1;
        break;
    case SpiTxIrq:
        //DBG_8195A("Master TX done!\n");
        MasterTxDone = 1;
        break;
    default:
        DBG_8195A("unknown interrput evnent!\n");
    }
}

/* выбор устройства
* ss_code - код для дешифратора,
*/
void spi0_slave_select(const uint8_t ssn)
{
    uint8_t ss_code;
    switch (ssn)
    {
    case 0:
        ss_code = 127;
        break;
    case 1:
        ss_code = ~1;
        break;
    case 2:
        ss_code = ~(1<<4);
        break;
    case 3:
        ss_code = ~((1<<4) | 1);
        break;
    case 4:
        ss_code = ~(1<<5);
        break;
    default :
        ss_code = 127;
    }; // switch
    // код CS_x
    // допустимый диапазон cs_code 1...0xff, если 0 - SPI зависает
    // *(volatile uint32_t*)(SSI0_REG_BASE + REG_DW_SSI_SER) = ss_code;  // задаем код слэйва (лучше использовать HAL_SSI_WRITE32())
    HAL_SSI_WRITE32(0, REG_DW_SSI_SER, ss_code);

}

static void spi0_hw_configure(void)
{
    char* buff;
    int32_t length;
    int32_t c;

    ConfigDebugInfo |= _DBG_SSI_;

    spi_init(&spi_master, SPI0_MOSI, SPI0_MISO, SPI0_SCLK, SPI0_CS); // CS заданный тут нигде не используется
    spi_format(&spi_master, 16, 0, 0);
    spi_frequency(&spi_master, 200000);

    // нужен только чтобы включить SPI0_MULTI_CS_EN в регистре SPI_MUX_CTRL,
    // число любое от 2 до 7
    //spi_slave_select(&spi_master, 2);

    buff = pvPortMalloc(256);
    c=0;
    do {
        //spi0_slave_select(c & 7);
        MasterTxDone = 0;
        //length= spi_master_write(&spi_master, c);
        spi_irq_hook(&spi_master, master_tr_done_callback, (uint32_t)&spi_master);
        spi_master_write_stream(&spi_master, buff, 160);
        while(MasterTxDone == 0) {
            //wait_ms(10);
        }

        rtl_printf("Master write: %d\n", c);
        c++;
    }
    while (1);

    free(buff);
}
Код:
===== Enter SRAM-Boot 1 ====
CPU CLK: 83333333 Hz, SOC FUNC EN: 0x20211113
Img Sign: RTKWin, Go @ 0x1000607d
===== Enter Image: tp6_rtl ====
[SSI  Inf]SystemClock: 166666666
[SSI  Inf]MaxSsiFreq : 20833333
[SSI  Inf]ssi_peri: 2049, ssi_idx: 0, ssi_pinmux: 1
[SSI  Inf]spi_frequency: Set SCLK Freq=200320
Init Heap Region: 0x10003000[12288]
Init Heap Region: 0x100460f8[171784]
[SSI  Inf]HalSsiIntWriteRtl8195a: Idx=0, RxData=0x10003008, Len=0xa0
 

sharikov

Active member
Про что вопрос то? Про spi_irq_hook(), spi_bus_tx_done_irq_hook(), ... или про RTL871x_Info/RTL871x_SPI.pdf at master · pvvx/RTL871x_Info · GitHub ?
Вопрос почему не работает.
spi_bus_tx_done_irq не вызывается, передача не начинается
потоковые операции с spi вообще кто-нибудь тестировал на свежем Sdk ? (они там были отключены)

есть подозрение что не работают прерывания от spi, но проверить не успел
 

pvvx

Активный участник сообщества
Вопрос почему не работает.
spi_bus_tx_done_irq не вызывается, передача не начинается
потоковые операции с spi вообще кто-нибудь тестировал на свежем Sdk ? (они там были отключены)

есть подозрение что не работают прерывания от spi, но проверить не успел
А зачем прерывания вообще при работе с SPI без DMA?
Выполнилась процедура передачи/приема - значит данные приняты/переданы. После неё и ставьте свой код.
По какой причине должен отрабатывать калбак на конец DMA, если DMA не используется? :confused:
 

pvvx

Активный участник сообщества
Вспомнил. Там только по прерываниям и работает Hal SPI в SDK.
В Arduino Ameba и возникает ошибка, если дать команду передачи блока, а CS там "ногодрыгом", в классическом стиле Arduino... :)
Пришлось добавить [inline]while(spi_busy((spi_t *)pSpiMaster));[/inline]
Код:
void SPIClass::transfer(byte _pin, void *_buf, size_t _count, SPITransferMode _mode)
{
    if (_pin != pinSS) {
        pinMode(_pin, OUTPUT);
        digitalWrite(_pin, 0);
    }
    spi_master_write_stream( (spi_t *)pSpiMaster , (char *)_buf, (uint32_t)_count );
    while(spi_busy((spi_t *)pSpiMaster));
    if (_pin != pinSS && _mode == SPI_LAST) {
        digitalWrite(_pin, 1);
    }
}
А в Ameba Arduino это так и не исправлено... Дуринщинкам всё равно всё пофиг.
 

pvvx

Активный участник сообщества
Всё работает:
Код:
/*
 * spi_test.c
 */
#include <platform_opts.h>
#include "rtl8195a.h"
#include "spi_api.h"
#include "spi_ex_api.h"
#include "rtl8195a/rtl_libc.h"

#define atoi(str)    prvAtoi(str)

#define SPI0_MOSI  PC_2
#define SPI0_MISO  PC_3
#define SPI0_SCLK  PC_1
#define SPI0_CS    PC_0

spi_t spi_master;

void fATSSI(int argc, char *argv[])
{
    int len = 128;
    int count = 32;
    int clk = 1000000;
    int ssn = 0;
    if(argc > 1) {
        len = atoi(argv[1]);
        if(len > 32768 || len <= 0) {
            len = 128;
            error_printf("%s: len = %u!\n", __func__, len);
        };
    };
    if(argc > 2) {
        count = atoi(argv[2]);
        if(count > 10000 || count <= 0) {
            count = 32;
            error_printf("%s: count = %u!\n", __func__, count);
        };
    };
    if(argc > 3) {
        clk = atoi(argv[3]);
        if(clk <= 0) {
            clk = 1000000;
            error_printf("%s: clk = %u!\n", __func__, clk);
        };
    };
    if(argc > 4) {
        ssn = atoi(argv[4]);
        if(ssn > 7 || ssn < 0) {
            ssn = 0;
            error_printf("%s: ssn = %u!\n", __func__, ssn);
        };
    };
    char* buff = pvPortMalloc(len);
    if(buff) {
        spi_init(&spi_master, SPI0_MOSI, SPI0_MISO, SPI0_SCLK, SPI0_CS); // CS заданный тут нигде не используется
        spi_format(&spi_master, 16, 0, 0);
        spi_frequency(&spi_master, clk);
          spi_slave_select(&spi_master, ssn);
          for(int i = 0; i < len; i++) buff[i] = (char)i;
        while(count--) {
            spi_master_write_stream(&spi_master, buff, len);
            while(spi_busy(&spi_master));
            rtl_printf("Master write: %d\n", count);
        };
        spi_free(&spi_master);
        free(buff);
    }
    else {
        error_printf("%s: error malloc!\n", __func__);
    }
}

MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_spitst[] = {
        {"ATSSI", 0, fATSSI, "[len[,count[,clk[,ssn]]]]: Spi test"}
};
 
Последнее редактирование:

sharikov

Active member
После старта rtos работает, до старта - нет.

Однако работу с spi придется писать "с нуля" по ДШ. Сейчас spi_master_write_stream передает блок кусочками размером transfer size с передергиванием CS между ними.
Например хотим передать одиночный блок 4 байта:
spi_format(&spi_master, 16, 0, 0);
spi_master_write_stream(&spi_master, buff, 4);
выдаст 2 пакета по 16 бит. А нужно 1 пакет 32 бита.

Причем беда как в mbed api так и на уровне hal.
mbed api заполняет не все поля структур hal;
hal недоделан, например не для всех вариантов заполняется регистр CTRL1
 
Последнее редактирование:

pvvx

Активный участник сообщества
После старта rtos работает, до старта - нет.
Ну и правильно. Как иначе то?
Однако работу с spi придется писать "с нуля" по ДШ. Сейчас spi_master_write_stream передает блок кусочками размером transfer size с передергиванием CS между ними.
Например хотим передать одиночный блок 4 байта:
spi_format(&spi_master, 16, 0, 0);
spi_master_write_stream(&spi_master, buff, 4);
выдаст 2 пакета по 16 бит. А нужно 1 пакет 32 бита.
Вы же сказали ему, что размерность 16 бит. Он и передает по 16 бит с CS.
Причем беда как в mbed api так и на уровне hal.
mbed api заполняет не все поля структур hal;
hal недоделан, например не для всех вариантов заполняется регистр CRTL1
Как-бы с VGA там сложнее, крутили разные варианты на API - всё работало. Там одно но - не задать циклические буфера, чтобы вечно и без паузы крутился.
С DMA CS ведет себя правильно. Без DMA SPI мне не нужен. Попробуйте на SPI1...
Мульти CS на SPI0 ведет себя не так, как хотелось бы и изменить это никак...
И почему используете только режимы spi_format(&spi_master, 16, 0, 0) и без DMA?

Там RTL8710 VGA Driver CS нормально формируется. Но пока, после сокращения размеров буферов не удалось убрать дрожание - привязку к синхро. Когда работают оба SPI и непрерывно, лопатя на выход ненужные фреймы синхронизации, то их синхронность как-то держится. Когда один SPI работет с перерывом, выдавая CS, то синхронность обоих SPI нарушается - бьет от времени старта загрузки регистров, отвлекаясь на другие задачи, а приоритеты всех прерываний, WiFi и прочего в SDK не изменить (очень долго и надо всё переписывать, а толку от этого может быть мало, т.к. бьет (дрожит) на десятки тактов CPU...).
 
Последнее редактирование:

pvvx

Активный участник сообщества
Если spi_format(&spi_master, 16, 3, 0), то CS ставиться на блок.
[inline]atssi 16 1 0 2[/inline]
Снимок1348.gif
В spi_format(&spi_master, 16, 1, 0) - тоже CS только на блок
В spi_format(&spi_master, 16 или 32, 0 или 2, 0) - CS каждые 16 бит, spi_format(&spi_master, 8, 0 или 2, 0) - каждые 8.
 
Последнее редактирование:

sharikov

Active member
С DMA CS ведет себя правильно. Без DMA SPI мне не нужен.
Поменял на DMA: абсолютно то же что по прерываниям.

Разрывы пропадают если задать
spi_format(&spi_master, X, 1, 0) или spi_format(&spi_master, X, 3, 0);
Размер посылки X не имеет значения, dma или прерывания тоже не имеет значения.

Читаю интеловский ДШ: не вижу возможности передавать длинные блоки без разрывов при аппаратном CS. Разве что какими нибудь ухищрениями явно не описанными в ДШ (так оно и получается на практике).
 

pvvx

Активный участник сообщества
Поменял на DMA: абсолютно то же что по прерываниям.

Разрывы пропадают если задать
spi_format(&spi_master, X, 1, 0) или spi_format(&spi_master, X, 3, 0);
Размер посылки X не имеет значения, dma или прерывания тоже не имеет значения.

Читаю интеловский ДШ: не вижу возможности передавать длинные блоки без разрывов при аппаратном CS. Разве что какими нибудь ухищрениями явно не описанными в ДШ (так оно и получается на практике).
В описании к ARM вообще 4..16 бит и CS -> ARM Information Center
 

nkly

New member
Как в RtlDuino сделать чтобы SS не дёргался при передаче двух блоков по 8 бит?

spi_format(&spi_master, X, 1, 0) spi_format(&spi_master, X, 3, 0); не помогает.
 

Вложения

sharikov

Active member
Как в RtlDuino сделать чтобы SS не дёргался при передаче двух блоков по 8 бит?
При передаче именно двух блоков SS обязан дергаться. Передавайте один блок 16 бит.

Вот так почему-то 15 вывод не дергается?
Должен дергаться. Проверьте не занят ли порт куда выведен 15 вывод.


---
SPI ужасен, хуже я еще не встречал. Менеджерам которые закупили именно этот IP блок гореть в аду!
Не предусмотрено прерывание по окончанию передачи (флаг SSI Busy), есть только прерывания по состоянию fifo. Без прерывания по окончанию передачи невозможна эффективная передача раздельных пакетов. В HAL опрашивают флаг в цикле в прерывании. В прерывании, Карл! Писателям HAL гореть в аду вот за это:
Код:
VOID _SsiWriteInterruptRtl8195a(VOID *Adapter)
...
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);
        for (i=0;i<1000000;i++) {          
            bus_busy = HAL_SSI_READ32(Index, REG_DW_SSI_SR) & BIT_SR_BUSY;
            if (!bus_busy) {
                break;  // break the for loop
            }
        }

        // If it's not a dummy TX for master read SPI, then call the TX_done callback
        if (pHalSsiAdapter->TxData != NULL) {
            if (pHalSsiAdapter->TxIdleCallback != NULL) {
                pHalSsiAdapter->TxIdleCallback(pHalSsiAdapter->TxIdleCbPara);
            }
        }
      
        return;
Для чтения небольших блоков данных (например показания ацп) проще использовать gpio и не связываться с кошмарным SPI вовсе.
 
Последнее редактирование:

pvvx

Активный участник сообщества
Для чтения небольших блоков данных (например показания ацп) проще использовать gpio и не связываться с кошмарным SPI вовсе.
А меня устраивает - всё что подключал изумительно работает.
Наверно пытаетесь подойти как в 8-ми битных MCU и писать всё сами, через регистры ногодрыгом без DMA, а с учетом RTOS...
Не предусмотрено прерывание по окончанию передачи (флаг SSI Busy), есть только прерывания по состоянию fifo. Без прерывания по окончанию передачи невозможна эффективная передача раздельных пакетов. В HAL опрашивают флаг в цикле в прерывании. В прерывании, Карл! Писателям HAL гореть в аду вот за это:
Код:
VOID _SsiWriteInterruptRtl8195a(VOID *Adapter)
...
        HalSsiSetInterruptMaskRtl8195a((VOID*)pHalSsiAdapter);
        for (i=0;i<1000000;i++) {     
            bus_busy = HAL_SSI_READ32(Index, REG_DW_SSI_SR) & BIT_SR_BUSY;
            if (!bus_busy) {
                break;  // break the for loop
            }
        }

        // If it's not a dummy TX for master read SPI, then call the TX_done callback
        if (pHalSsiAdapter->TxData != NULL) {
            if (pHalSsiAdapter->TxIdleCallback != NULL) {
                pHalSsiAdapter->TxIdleCallback(pHalSsiAdapter->TxIdleCbPara);
            }
        }
 
        return;
Это задержка по причине того, что точно до такта, прерывания и защелку FIFO пользователь не задает, с учетом и частоты CPU и SPI, и приоритетов прерываний.
Сделано то это исключительно для тупых пользователей перед вызовом пользовательского калбака. :) Вдруг пользователь туда коммутацию CS впишет ногодрыгом?
Изменение там числа от 100 до 1000000 не влияет на более сложных задачах...
Ставьте правильно регистры SPI - задержка ожидания передачи последних бит и не потребуется.
Возьмите HAL и примеры от Intel D2000 - там ещё прикольнее - SPI и DMA те-же..
 
Последнее редактирование:

sharikov

Active member
А меня устраивает - всё что подключал изумительно работает.
Что именно вы подключали ?
Подключение к шине SPI множества слэйвов с раздельными CS реализовывали ?
Гнать непрерывный битстрим и адресно общаться с множеством устройств на шине - разные задачи.

Это задержка по причине того, что точно до такта, прерывания и защелку FIFO пользователь не задает, с учетом и частоты CPU и SPI, и приоритетов прерываний.
Эту задержку некуда задавать: в блоке spi не так много регистров чтобы что-то пропустить. Кроме того задержку в тактах процессора точно вычислить нельзя - она плавает.
Вы случаем не путаете SSI и SPIC ? Мы о первом разговариваем.

Сделано то это исключительно для тупых пользователей перед вызовом пользовательского калбака. :) Вдруг пользователь туда коммутацию CS впишет ногодрыгом?
Именно коммутацию CS и требуется выполнить. Только не ногодрыгом а через регистр SER с использованием MULTU CS.
Чтобы работал MULTU CS регистр SER нужно обновлять в состоянии Idle а его определить можно только опросом регистра статуса. Финиш!
Хотелось переключать в прерывании чтобы работало быстро.
Подобную тему поднимал kissste 30 декабря прошлого года [SPI DMA and toggling CS], решения нет.

Также API не позволяет перезапустить spi из callback - ругается [SSI Wrn]spi_master_write_and_read_stream: state(0x2) is not ready.
Пришлось завести семафор и запускать spi обмен в основном потоке, это занимает 24 микросекунды или более в зависимости от работы планировщика rtos при том что данные для обмена заранее подготовлены. Древний AVR передаст быстрее а тут процессор на 160MHz.

Пример: используется spi0, тактовая spi 200kHz (нужна низкая частота)
подключено 3 шт. M25P10, cs подключены на SPI0_CS0 - SPI0_CS2.
Требуется быстро прочитать STATUS REGISTER у всех трех. Ограничение: "чужой" трафик и лишние байты на чипы не гнать, т.е прочитали ровно то что требуется и переключили CS на следующий чип.
Обмен spi: CS(n)=Low [0x05 0x00 0x00 IN] CS(n)=High, n=0...2

примеры от Intel D2000 - там ещё прикольнее - SPI и DMA те-же..
Что-то не ищутся примеры на D2000. То что есть на форумах обычный ардуино стиль без всяких dma с ручным ногодрыгом cs.
 

pvvx

Активный участник сообщества
Что именно вы подключали ?
Подключение к шине SPI множества слэйвов с раздельными CS реализовывали ?
Гнать непрерывный битстрим и адресно общаться с множеством устройств на шине - разные задачи.
Подключал ADC типа AD7176 и ещё наверно буду другие.
Вы случаем не путаете SSI и SPIC ? Мы о первом разговариваем.
Нет - не путаю. Не задержка, а счетчики в FIFO до срабатывания прерывания.
Что-то не ищутся примеры на D2000. То что есть на форумах обычный ардуино стиль без всяких dma с ручным ногодрыгом cs.
Про то и разговор - никто не использует в процессорах общего применения специфические функции контроллеров. Если хотите их использовать - сначала изучите. А то разводите нытьё, вместо решения.
Вам показал, что в разных режимах контроллера по разному срабатывают CS.
C мульти-CS, почему написал не очень - при включении используются все линии CS выходящие из чипа и на них всех происходят переключения при активации- деактивации SPI с включенным мульти-CS.
 
Последнее редактирование:

pvvx

Активный участник сообщества
Пример: используется spi0, тактовая spi 200kHz (нужна низкая частота)
подключено 3 шт. M25P10, cs подключены на SPI0_CS0 - SPI0_CS2.
Требуется быстро прочитать STATUS REGISTER у всех трех. Ограничение: "чужой" трафик и лишние байты на чипы не гнать, т.е прочитали ровно то что требуется и переключили
M25P10A поддерживает режим 0 и 3. В режиме 3 и использовании имеющегося HAL CS выставляется как надо для M25P10A - блоками.
Есть 3 калбака - по окончанию передачи блока (мульти-блочном DMA), полного окончания передачи, конца приема. Что ещё то надо? :confused:
 

pvvx

Активный участник сообщества
Также API не позволяет перезапустить spi из callback - ругается [SSI Wrn]spi_master_write_and_read_stream: state(0x2) is not ready.
Пришлось завести семафор и запускать spi обмен в основном потоке, это занимает 24 микросекунды или более в зависимости от работы планировщика rtos при том что данные для обмена заранее подготовлены. Древний AVR передаст быстрее а тут процессор на 160MHz.
А ещё более древние i386ex - дергают ножками портов быстрее, чем любой современный 8-ми головый i7 на 4 ГГц :)
i7 сравним по этому с i8086 на 10 MГц.
Такие вот сравнения у вас.
Это может значить одно - не справились с задачей - не тот контроллер поставили, не ту ОС используете, не того разработчика выбрали :) Ставьте AVR. :)
Лично для вас, макетка, из того века (1995г вроде) для теста скорости "ногодрыга":
Снимок1351.jpg
Представляете, даже в ВыньXP вывод в порт (пусть LPT), сопровождается переключением уровня привилегий до дна у процов cГГц-ами, а затем передачи параметров драйверу, с полными объявами невалидности всех кешей (пару накопленных мегабайт в утиль). Далее, через шину PCI, запрос пойдет (на старых мамках Intel) в аппаратный эмулятор контроллера ISA шины, тактируемый 14 МГц и на это время все процессы встанут от поставленного на ней сигнала READY (пока не описали новую процессорную шину и PCI :) с отложенными транзакциями)… А тот-же i386ex дергает портом с частотой своей шины :)
Это аналог вашего сравнения с AVR. :)
 
Последнее редактирование:

Simon

Member
Пытаюсь запустить с rtl синхронизирующий сигнал 200кгц(шим, 50% заполнение). Попробовал использовать CLK от SPI0(передаю что угодно, беру CLK как нужный мне шим). Чтобы не было пропусков синхронизации(CLK работал непрерывно), запускаю передачу заново по "прерыванию окончания передачи".

Проблема:
Как только входит в обработчик, в консоли сообщение:
RTL8195A[HAL]: ISR 0 had been allocated!!!
И wifi падает. Видимо, почему-то используется какое-то прерывание, которое нужно wifi. SPI0 при этом работает, как я хочу.

Делаю так:
1) обработчик прерываний привязываю через spi_irq_hook()
2) в обработчике прерываний использую условие if (event == SpiTxIrq) (прерывание по концу передачи)
3) запускаю передачу через spi_master_write_stream_dma

Пробовал по образцу pvvx в теме VGA
https://esp8266.ru/forum/threads/rtl8710-vga-driver.1914/#post-33439

использовать упрощенную spi_master_write_stream_dma2 и запреты прерываний в обработчике (portDISABLE_INTERRUPTS();), не помогло.

Как сохранить работающий wifi? Или может есть другой, не съедающий ресурсов способ генерировать 200кгц, 50% duty?
 
Последнее редактирование:
Сверху Снизу