• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе 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?
 
Последнее редактирование:
Сверху Снизу