Вопросы и замечания по статьям и схемам, представленным на нашем сайте

При поддержке РадиоКОТструктор.ру


Ответить

stm32f103 + w5500, spi

Пт мар 08, 2024 01:39:07

Доброго время суток!

В статье:
https://www.radiokot.ru/artfiles/6740/
не работает ссылка на исходники

Интересует (для stm32f103c8t6), если кто-нибудь может подсказать:
- реализация функций чтения и записи данных по SPI для функции reg_wizchip_spi_cbfunc(...)
- инициализация SPI

Re: stm32f103 + w5500, spi

Вс мар 10, 2024 02:45:12

Похоже, что в библиотеке wiznet для W5500 есть простой режим с блокирующей передачей по одному байту через SPI. Тогда функции могут быть устроены следующим образом:

Код:
// Принять байт через SPI.
static uint8_t
spi2_read_byte() {
    // Ждем, пока не освободится буфер передатчика.
    while ( !(SPI2->SR & SPI_SR_TXE) ) {
    }

    SPI2->DR = 0; // запускаем обмен

    // Ждем, пока не появится новое значение в буфере приемника.
    while ( !(SPI2->SR & SPI_SR_RXNE) ) {
    }

    // Возвращаем значение буфера приемника.
    return SPI2->DR;
}
 
// Передать байт через SPI.
static void
spi2_write_byte( uint8_t v) {
    // Ждем, пока не освободится буфер передатчика.
    while ( !(SPI2->SR & SPI_SR_TXE) ) {
    }

    // Заполняем буфер передатчика.
    SPI2->DR = v;

    // Ждем, пока не появится новое значение в буфере приемника.
    while ( !(SPI2->SR & SPI_SR_RXNE) ) {
    }

    return;
}


Режим блокирующей передачи выбирается благодаря вызову:

Код:
    ...
    reg_wizchip_spi_cbfunc( &spi2_read_byte, &spi2_write_byte);
    ...


Соответственно осталось разобраться как правильно инициализировать SPI2 и выполнить остальную настройку wiznet.

Re: stm32f103 + w5500, spi

Вт мар 12, 2024 02:48:45

Не понял почему, но если вдруг пригодится, то пришлось вставить задержки (минимальное значение задержки не подбирал):

Код:
// Принять байт через SPI.
static uint8_t
spi2_read_byte() {
    int32_t i;
    uint8_t v = 0;

    for ( i = 0; i < 1000; ++i ) ;

    // Ждем, пока не освободится буфер передатчика.
    while ( !(SPI2->SR & SPI_SR_TXE) )  ;

    SPI2->DR = 0; // запускаем обмен

    // Ждем, пока не появится новое значение в буфере приемника.
    while ( !(SPI2->SR & SPI_SR_RXNE) )  ;

    // Получаем значение буфера приемника.
    v = SPI2->DR;

    for ( i = 0; i < 1000; ++i ) ;

    return (v);
}


Аналогично, добавлена задержка во вторую функцию:

Код:
 
// Передать байт через SPI.
static void
spi2_write_byte( uint8_t v) {
    int32_t i;

    for ( i = 0; i < 1000; ++i ) ;

    // Ждем, пока не освободится буфер передатчика.
    while ( !(SPI2->SR & SPI_SR_TXE) ) ;

    // Заполняем буфер передатчика.
    SPI2->DR = v;

    // Ждем, пока не появится новое значение в буфере приемника.
    while ( !(SPI2->SR & SPI_SR_RXNE) )  ;

    return;
}

Re: stm32f103 + w5500, spi

Ср мар 13, 2024 06:48:10

И для чего-же понадобились задержки?
STM32F446 общается с W5500 на 22.5МГц без всяких задержек.

Re: stm32f103 + w5500, spi

Ср мар 13, 2024 13:19:28

Изображение

Добавлено after 14 minutes 42 seconds:
И для чего-же понадобились задержки?
STM32F446 общается с W5500 на 22.5МГц без всяких задержек.


Оба варианта не рабочие. (Начинаю разбираться в программировании микроконтроллеров в принципе и stm32 в частности.)

На картинке отправка двух байт. Желтый - это программный строб (0 - выбор устройства). Синий - тактирующий сигнал для передачи 2 байт. Как видно на осциллограмме передача данных продолжается после выхода из функции записи байта. Вот простой пример, для которого получена осциллограмма:

Код:
    while ( 1 ) {
        GPIOB->BRR = GPIO_Pin_12;
        spi2_write_byte( 0x5a);
        spi2_write_byte( 0xa5);
        GPIOB->BSRR = GPIO_Pin_12;
        for ( j = 0; j < 1000; ++j ) ;
    }



Буду пробовать теперь следующий вариант:

Код:
// Принять байт через SPI.
static uint8_t
spi2_read_byte() {
    uint8_t v = 0;

    // Start data exchange.
    spi2_write_byte( 0x5a);

    // Get input value from the r-buffer.
    v = SPI2->DR;

    return (v);
}


Код:
// Передать байт через SPI.
static void
spi2_write_byte( uint8_t v) {
    // Put output value to the w-buffer.
    SPI2->DR = v;

    // Wait until w-buffer will be ready.
    while ( !(SPI2->SR & SPI_SR_TXE) ) ;

    // Wait until the transmition to be completed.
    while ( (SPI2->SR & SPI_SR_BSY) ) ;

    return;
}


Как минимум выход из функции записи байта теперь происходит после завершения отправки байта. Но чтобы проверить этот вариант для W5500 надо припаять обратно один оторвавшийся при снятии осциллограммы проводок. Использование двух последовательных проверок SPI_SR_TXE и SPI_SR_BSY "подсмотрел" на сайте stm'а (https://community.st.com/t5/stm32-mcus- ... d-p/483106):

During discontinuous communications, there is a 2 APB clock period delay between the write operation to SPI_DR and the BSY bit setting. As a consequence, in transmit-only mode, it is mandatory to wait first until TXE is set and then until BSY is cleared after writing the last data.


Добавлено after 9 minutes 1 second:

Добавлено after 52 seconds:
Для снятия осциллограммы к слову использовал 1014D (это в принципе мой первый собственный осциллограф). Сначала "люто" в чат-споре ругался и требовал возврата. Но как оказалось любым инструментом надо сначала научиться пользоваться, иначе получается как с мартышкой, надевший очки на хвост. Хотя сейчас может быть взял бы и более дорогую модель (но не сильно дороже :) ).

Добавлено after 50 minutes:
К слову сказать, "удешевление" макетной платы или изобретение велосипеда и кручение педалей руками :)

Изображение Изображение

Re: stm32f103 + w5500, spi

Ср мар 13, 2024 18:14:05

siderta писал(а):(Начинаю разбираться в программировании микроконтроллеров в принципе и stm32 в частности.)

Козырный заход: не зная ни STM32, ни W5500 сразу начинать работать с W5500 через его регистры. Может, просто взять библиотеку от Wiznet и начать работать с ней? Тут где-то roman.ru выкладывал свои результаты по работе с W5500 через его регистры. Может, сначала изучить его труды?

Надо не задержку делать, а читать регистр состояния W5500. И делать это не _после_ отправки команды, а _перед_, ибо пока готовится отправка очередной команды, предыдущая команда, скорей всего, уже выполнится. Может, почитать мурзилку от Визнета, изучить исходники их библиотеки, почитать тему с работой Романа?

Re: stm32f103 + w5500, spi

Ср мар 13, 2024 23:17:12

siderta писал(а):...

Козырный заход: не зная ни STM32, ни W5500 сразу начинать работать с W5500 через его регистры. Может, просто взять библиотеку от Wiznet и начать работать с ней? Тут где-то roman.ru выкладывал свои результаты по работе с W5500 через его регистры. Может, сначала изучить его труды?

Надо не задержку делать, а читать регистр состояния W5500. И делать это не _после_ отправки команды, а _перед_, ибо пока готовится отправка очередной команды, предыдущая команда, скорей всего, уже выполнится. Может, почитать мурзилку от Визнета, изучить исходники их библиотеки, почитать тему с работой Романа?


1) Светодиодом помигал. Частоту в 72МГц, далеко не сразу, но настроил (там в конце концов оказалось, что флюс остался на кварце). Печать через UART заработала. Теперь нужно, чтобы заработало сопряжение через Ethernet с помощью W5500. Да приходится учиться новому, а как иначе-то?

2) "Почитать тему от Романа" - а где ее можно почитать? И кто такой Роман :)?

3) С W5500 так раз и работаю через их wiznet библиотеку, пытаясь скопировать код из разных примеров. Например, вот этого https://github.com/IOsetting/stm32f103- ... ware/spi.c (примеров для blue pill и w5500 кстати очень мало находится). Но только код для W5500 у меня пока не завелся. Стал разбираться в причинах, через некоторое время залез внутрь функций wizchip_setnetinfo/wizchip_getnetinfo. Нашел там функции setSIPR/getSIPR. Сделал из них маленький примерчик с чтением ip-адреса и установки ip-адреса. Вот на нем пока и отлаживаюсь. (Упрощал пример еще дальше, чтобы просто посмотреть на работу spi.) Так что запись/чтение регистров W5500 взято для выделения маленького отладочного примера на основе wiznet-библиотеки. Наверное, придется пойти и на форум к wiznet'у.


Кстати, отладка на маленьком примере приводит к следующему выводу. Функция setSIPR пишет 3 байта с адресом wiznet-регистра SIPR и пишет 4 байта с новым значением ip-адреса для w5500. Функция getSIPR пишет 3 байта с адресом wiznet-регистра SIPR и читает 4 байта с ip-адресом из w5500.

Так вот. Пробую записать ip-адрес A.B.C.D, затем прочитать ip-адрес. Но в моем случае при моих реализациях read/write функция getSIPR возвращает 1.B.C.D, причем осцилограмма похоже говорит о том, что от w5500 возвращается также A.B.C.D, но вместо A всегда получается 1. Видимо у меня по-прежнему в коде какие-то баги с настройкой SPI и/или функциями read/write, которые передаются в wiznet-библиотеку в качестве параметром при настройке библиотеки.

Добавлено after 7 minutes 17 seconds:
отладочный примерчик для записи ip-адреса в W5500 и чтения ip-адреса из W5500:

Код:
    while ( 1 ) {
        int i;
        uint8_t defip[4] = {0, 0, 0, 0};

        for ( i = 0; i < 1*1000; ++i ) ;
        getSIPR( defip);
#if 0
        usart_print_string( USART1, "wiznet_init: ");
        usart_print_number( USART1, defip[0]);
        usart_print_string( USART1, ".");
        usart_print_number( USART1, defip[1]);
        usart_print_string( USART1, ".");
        usart_print_number( USART1, defip[2]);
        usart_print_string( USART1, ".");
        usart_print_number( USART1, defip[3]);
        usart_print_string( USART1, "\n");
#endif
        for ( i = 0; i < 1*100; ++i ) ;
        setSIPR( netInfo.ip);
    }


Добавлено after 15 minutes 3 seconds:
ВОТ КСТАТИ.

Записал с stm32 ip-адрес 192.168.10.20 в w5500, подключил w5500 к компьютеру и с компьютера можно ПИНГОВАТЬ 192.168.10.20!!! (Раньше пинга не было.) То есть как минимум команды записи в w5500 проходят. Осталось разобраться, что не так с командами чтения с w5500.

Добавлено after 1 hour 23 minutes 32 seconds:
Подсмотрел еще в одном проекте.

Код:
 
// Передать байт через SPI и вернуть полученный по SPI байт.
static uint8_t
spi2_write_read_byte( uint8_t v) {
    // Put output value to the w-buffer.
    SPI2->DR = v;

    // Wait until w-buffer will be ready.
    while ( !(SPI2->SR & SPI_SR_TXE) ) ;

    // Wait until the transmition to be completed.
    while ( (SPI2->SR & SPI_SR_BSY) ) ;

    // Wait until r-buffer will be ready.
    while ( !(SPI2->SR & SPI_SR_RXNE) ) ;

    // Get input value from the r-buffer.
    v = SPI2->DR;

    return (v);
}


Тогда функции чтения и записи байта становятся просто вызовами одной и той же функции:

Код:
 
// Принять байт через SPI.
static uint8_t
spi2_read_byte() {
    uint8_t v = spi2_write_read_byte( 0x5a);

    return (v);
}
 
// Передать байт через SPI.
static void
spi2_write_byte( uint8_t v) {
    spi2_write_read_byte( v);

    return;
}


Все дело в том, что при записи все равно приходит ответный байт. И если от последней записи этот полученный байт не забрать, то ближайшее следующее чтение возьмет неверное значение. Получается, что операции записи в SPI->DR и чтения из SPI->DR должны быть парными?

Re: stm32f103 + w5500, spi

Чт мар 14, 2024 08:35:54

ППЦ.
Просто определи функции доступа к SPI.
Спойлер
Код:
////////////////////////////////////////////////////////////////////////////////
// предоставляем библиотеке возможности:

void cris_en( void )
{
    semaphore -> takeWithBlock();
}

void cris_ex( void )
{
    semaphore -> give();
}

////////////////////////////////////////////////////////////////////////////////
// Включить модуль W5500 сигналом SCNn=0

void cs_sel( void )
{
    cs_pin -> write( OFF );
}

////////////////////////////////////////////////////////////////////////////////
// Выключить модуль W5500 сигналом SCNn=1

void cs_desel( void )
{
    while( wiz_spi -> SR & SPI_SR_BSY )
    {
    }

    cs_pin -> write( ON );
}

////////////////////////////////////////////////////////////////////////////////
// Цикл обмена по SPI

inline uint8
spi_exchange( uint8 _aByte )
{
    uint8
        tmp8;

    uint32
        prot;

    prot = 10'000'000;

    while( wiz_spi -> SR & SPI_SR_RXNE )
    {
        tmp8 = wiz_spi -> DR;

        prot--;

        if( prot == 0 )
        {
            break;
        }
    };

    *( volatile uint8 *)&( wiz_spi -> DR) = _aByte;

    prot = 10'000'000;

    while( !( wiz_spi -> SR & SPI_SR_RXNE ) )
    {
        prot--;

        if( prot == 0 )
        {
            break;
        }
    }

    tmp8 = *( volatile uint8*)&( wiz_spi -> DR );

    return( tmp8 );
}

////////////////////////////////////////////////////////////////////////////////
// принять байт через SPI

uint8
spi_rb( void )
{
    return( spi_exchange( 0 ) );
}

////////////////////////////////////////////////////////////////////////////////
//  передать байт через SPI

void
spi_wb( uint8_t _aByte )
{
    spi_exchange( _aByte );
}

////////////////////////////////////////////////////////////////////////////////


И зарегистрируй их в библиотеке.
Спойлер
Код:
    reg_wizchip_cs_cbfunc( cs_sel, cs_desel );
    reg_wizchip_spi_cbfunc( spi_rb, spi_wb );


Блин, ну всё ведь расписано-разжёвано в туевой хуче примеров.

Re: stm32f103 + w5500, spi

Чт мар 14, 2024 16:01:30

ну да, ну да, только ложка дорога к обеду :)))
Ответить