реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> STM32F3 SPI, Непонятное поведение
bseyur
сообщение Jul 1 2013, 11:10
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 65
Регистрация: 8-01-07
Из: Томск
Пользователь №: 24 208



Добрый день! Нужна помощь решении проблемы с SPI микроконтроллера STM32F303.

Начал работать с семейством STM32 впервые, до этого плотно заимался с продукцией NXP. Хоть и без негативных эмоций не обошлось, но на чипе без особых проблем удалось запустить ядро, USART, DMA, пару таймеров. А вот с SPI вошел в ступор.

Работаем в режиме мастера. Симптомы наблюдаются следующие:
1) При передаче блока длиной 16 бит в режиме "MSB first" первым почему-то уходит младший байт, затем старший. Такое впечатление, будто ядро little-endian, а блок SPI big-endian. Если это фича, то я не понял ее смысла.
2) SPI странно реагирует на установку размера блока данных (биты 8-11 в регистре CR2). Если в регистр записать значение (7<<8), что соответсвует 8 битам данных, в действительности блок содержит 16 бит. 8 бит можно получить, если установить значение 3<<8, 4 бит. Для эксперимента в регистр SPI->DR производится запись только 1 раз.
3) При попытке принять данные с внешнего устройства считанное значение не соответствует реальному.

Подозреваю, все три случая как-то связаны между собой, но уже не знаю, куда копать. Процедуру инициализации писал сам, глядя в даташит и stmlib, готовые функции используются только для инициализации портов в/в и включения тактовых клоков. На одном модуле SPI сидит 5 устройств, конфигурация у каждого своя, поэтому содержимое регистров CR1 и CR2 для них загружается из выделенных ранее переменных. Оптимизация выключена.

В момент записи в регистр DR конфигурация такая:
CR1 = 0x36F, установлены в 1 биты SPE SSM SSI MSTR CPOL CPHA, BR=5, остальные 0
CR2 = 0x700, все биты равны 0, кроме поля Data size (должно быть 8 бит)

Если кто-то сталкивался с подобным поведением, подскажите, в каком направлении двигаться? sm.gif

Заполнение полей конфигурации для каждого устройства:
Код
TDeviceInfo* devinfo_ptr = SPIdeviceListPtr + device_id;
devinfo_ptr->CR1 = devinfo_ptr->CR2 = 0;
devinfo_ptr->CSEnablePtr = device_info->CSEnablePtr;
devinfo_ptr->CSDisablePtr = device_info->CSDisablePtr;
if (device_info->LSBFirst)
    devinfo_ptr->CR1 |= SPI_FirstBit_LSB;
devinfo_ptr->CR1 |= device_info->BaudRateControl&(7UL<<3);
if (!device_info->SlaveMode)
    devinfo_ptr->CR1 |= SPI_Mode_Master | SPI_NSS_Soft;
if (device_info->CPOL)
    devinfo_ptr->CR1 |= SPI_CPOL_High;
if (device_info->CPHA)
    devinfo_ptr->CR1 |= SPI_CPHA_2Edge;
devinfo_ptr->CR2 |= device_info->DataSize;


Настройка блока SPI:
Код
TDeviceInfo* devinfo_ptr = SPIdeviceListPtr + device_id;
SPI_TypeDef* SPIRegs;
if (SPINumber==SPIDRV_DEVICE1_INDEX)
    SPIRegs = SPI1;
else if (SPINumber==SPIDRV_DEVICE2_INDEX)
    SPIRegs = SPI2;

SPIRegs->CR1 &= ~SPI_CR1_SPE;
SPIRegs->CR1 = devinfo_ptr->CR1;
SPIRegs->CR2 = devinfo_ptr->CR2;
SPIRegs->CR1 = devinfo_ptr->CR1;
SPIRegs->CR2 &= ~SPI_CR2_DS;
SPIRegs->CR2 = devinfo_ptr->CR2;
SPIRegs->CR1 |= SPI_CR1_SPE;


Процедура отправки 16 бит данных в виде "2 раза по 8 бит":
Код
void CSPIDriver::SendData(uint16_t data)
    {
    SPI_TypeDef* SPIRegs;
    if (SPINumber==SPIDRV_DEVICE1_INDEX)
        SPIRegs = SPI1;
    else if (SPINumber==SPIDRV_DEVICE2_INDEX)
        SPIRegs = SPI2;

    uint8_t byte1;
    uint8_t byte2;
    if ((SPIdeviceListPtr+CurrentDeviceID)->CR1&SPI_CR1_LSBFIRST)
        {
        byte1 = ((uint8_t*)&data)[0];
        byte2 = ((uint8_t*)&data)[1];
        }
    else
        {
        byte1 = ((uint8_t*)&data)[1];
        byte2 = ((uint8_t*)&data)[0];
        }

    while (SPIRegs->SR&SPI_SR_BSY);
    while (SPIRegs->SR&SPI_SR_RXNE)
        {volatile uint32_t dummy = SPIRegs->DR;}

    SPIRegs->DR = byte1;
    SPIRegs->DR = byte2;
    while (!(SPIRegs->SR&SPI_SR_TXE));
    }


Процедура приема данных:
Код
int32_t CSPIDriver::ReadMass(uint8_t* data, uint32_t count)
    {
    if (!data || !count)
        return 0;
    SPI_TypeDef* SPIRegs;
    if (SPINumber==SPIDRV_DEVICE1_INDEX)
        SPIRegs = SPI1;
    else if (SPINumber==SPIDRV_DEVICE2_INDEX)
        SPIRegs = SPI2;

    while (SPIRegs->SR&SPI_SR_BSY);
    while (SPIRegs->SR&SPI_SR_RXNE)
        {volatile uint32_t dummy = SPIRegs->DR;}

    uint32_t items_sent = 0;
    uint32_t items_recieved = 0;
    while (items_recieved<count)
        {
        while (items_sent<count && (SPIRegs->SR&SPI_SR_FTLVL)!=SPI_TransmissionFIFOStatus_Full)
            {
            SPIRegs->DR = 0xFF;
            items_sent++;
            }
        while (SPIRegs->SR&SPI_SR_RXNE)
            data[items_recieved++] = SPIRegs->DR;
        }

    return items_recieved;
    }
Go to the top of the page
 
+Quote Post
adnega
сообщение Jul 1 2013, 11:52
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702



Цитата(bseyur @ Jul 1 2013, 15:10) *
Если кто-то сталкивался с подобным поведением, подскажите, в каком направлении двигаться? sm.gif

Многое зависит от того каким образом вы записываете данные в регистр DR.
Если число бит в посылке от 4 до 8, то к нему нужно обращаться как к байту (т.е. читать и писать uint8_t).
Если число бит в посылке от 9 до 16, то к нему нужно обращаться как к полуслову (т.е. читать и писать uint16_t).
Для этого в StdLib я поправил структуру для SPI:
CODE

/**
* @brief Serial Peripheral Interface
*/

typedef struct
{
__IO uint16_t CR1; /*!< SPI Control register 1 (not used in I2S mode), Address offset: 0x00 */
uint16_t RESERVED0; /*!< Reserved, 0x02 */
__IO uint16_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */
uint16_t RESERVED1; /*!< Reserved, 0x06 */
__IO uint16_t SR; /*!< SPI Status register, Address offset: 0x08 */
uint16_t RESERVED2; /*!< Reserved, 0x0A */
union
{
__IO uint16_t DR16; /*!< SPI data register, Address offset: 0x0C */
__IO uint8_t DR8; /*!< SPI data register, Address offset: 0x0C */
};

uint16_t RESERVED3; /*!< Reserved, 0x0E */
__IO uint16_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */
uint16_t RESERVED4; /*!< Reserved, 0x12 */
__IO uint16_t RXCRCR; /*!< SPI Rx CRC register (not used in I2S mode), Address offset: 0x14 */
uint16_t RESERVED5; /*!< Reserved, 0x16 */
__IO uint16_t TXCRCR; /*!< SPI Tx CRC register (not used in I2S mode), Address offset: 0x18 */
uint16_t RESERVED6; /*!< Reserved, 0x1A */
__IO uint16_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */
uint16_t RESERVED7; /*!< Reserved, 0x1E */
__IO uint16_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */
uint16_t RESERVED8; /*!< Reserved, 0x22 */
} SPI_TypeDef;



В зависимости от разрядности обращаюсь либо к SPIx->DR8, либо SOIx->DR16.
Go to the top of the page
 
+Quote Post
bseyur
сообщение Jul 1 2013, 12:09
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 65
Регистрация: 8-01-07
Из: Томск
Пользователь №: 24 208



Благодарю за ответ! Завтра попробую.
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Jul 1 2013, 12:42
Сообщение #4


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



1) Как вы к data ragister будете обращаться ниаче, чем описано в хедерах? ССЗБ!
2) Попробуйте функции разбиения на байты (и сборки слов) вместо применённых конструкций переписать в виде сдвигов, умножений, делений.

upd: посмотрел код ещё раз... Нет необходимости вообще в коде программы менять байты!
Код
    if ((SPIdeviceListPtr+CurrentDeviceID)->CR1&SPI_CR1_LSBFIRST)
        {
        byte1 = ((uint8_t*)&data)[0];
        byte2 = ((uint8_t*)&data)[1];
        }
    else
        {
        byte1 = ((uint8_t*)&data)[1];
        byte2 = ((uint8_t*)&data)[0];
        }

Просто передавайте data в 16-ти битном режиме.
Go to the top of the page
 
+Quote Post
adnega
сообщение Jul 1 2013, 12:45
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702



Цитата(Genadi Zawidowski @ Jul 1 2013, 16:29) *
1) Как вы к data ragister будете обращаться ниаче, чем описано в хедерах? ССЗБ!

Ну... тут либо правленые хедеры и отсутствие совместимости, либо исползование стандартной библиотекой со всеми вытекающими
Цитата
[..] The read access of the SPI_DR register can be done using
SPI_ReceiveData8() (when data size is equal or inferior than 8bits) and.
SPI_I2S_ReceiveData16() (when data size is superior than 8bits)function
and returns the Rx buffered value. Whereas a write access to the SPI_DR
can be done using SPI_SendData8() (when data size is equal or inferior than 8bits)
and SPI_I2S_SendData16() (when data size is superior than 8bits) function
and stores the written data into Tx buffer.

Код
void SPI_SendData8(SPI_TypeDef* SPIx, uint8_t Data)
{
  uint32_t spixbase = 0x00;
  /* Check the parameters */
  assert_param(IS_SPI_ALL_PERIPH(SPIx));
  spixbase = (uint32_t)SPIx;
  spixbase += 0x0C;
  *(__IO uint8_t *) spixbase = Data;
}


Но это вопрос второй. Главное, что поведение SPI в F3 зависит от того, как к ним обращаться))
Go to the top of the page
 
+Quote Post
bseyur
сообщение Jul 1 2013, 13:12
Сообщение #6


Участник
*

Группа: Участник
Сообщений: 65
Регистрация: 8-01-07
Из: Томск
Пользователь №: 24 208



Цитата(Genadi Zawidowski @ Jul 1 2013, 19:42) *
....
Просто передавайте data в 16-ти битном режиме.

Изначально я так и делал. Только в 16-битной передаче контроллер почему-то меняет местами младший и старший байты, т.е. таким образом: D7-D0, D15-D8. Но должна идти старшим битом вперед! Соответственно пришлось поменять байты вручную.
Я до сих пор не понимаю, почему так!
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Jul 1 2013, 16:17
Сообщение #7


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



ну как бы в литле ендиан сначала идет младший байт вроде как...
он в память наверное по 8 бит передает, через указатель, вот сначала и ушел младший байт...
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Jul 1 2013, 17:47
Сообщение #8


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



Цитата(bseyur @ Jul 1 2013, 17:12) *
Изначально я так и делал. Только в 16-битной передаче контроллер почему-то меняет местами младший и старший байты, т.е. таким образом: D7-D0, D15-D8. Но должна идти старшим битом вперед! Соответственно пришлось поменять байты вручную.
Я до сих пор не понимаю, почему так!


Судя по даташиту, в режиме LSB FIRST пойдёт DO...D7, затем D8..D15 после одной записи в регистр данных. Я использовал только 16-ти битный MSB FIRST, в котором идёт D15..D0
Go to the top of the page
 
+Quote Post
bseyur
сообщение Jul 2 2013, 05:08
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 65
Регистрация: 8-01-07
Из: Томск
Пользователь №: 24 208



Цитата(adnega @ Jul 1 2013, 18:52) *
Многое зависит от того каким образом вы записываете данные в регистр DR.
.....

Действительно, как только я начал обращаться к регистру DR как к байту, все глюки ушли. Благодарю за наводку. sm.gif Порядок байт теперь также верный.
По правде говоря, сначала я посчитал эту версию несостоятельной, к примеру, чипы NXP честно отбрасывают лишние разряды. Но позже увидел такую расплывчатую формулировку:
Цитата
When the SPIx_DR register is accessed, data frames are always right-aligned
into either a byte (if the data fits into a byte) or a word (see Figure 293). During
communication, only bits within the data frame are clocked and transferred.

Спрашивается, для чего тогда необходимо поле Data size?
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Sep 13 2013, 20:08
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



Бились сегодня с подобной проблемой на LPC1678, думаю механизмы SPI схожи
если в регистр SPI пихать данные типа так
SPI -> DR = 0x00FF, уходит верно

а если иметь char buf[10];
short int *buf_p =(short int *)buf;
SPI -> DR = buf_p++;
то уходит в поменянном порядке байт, то есть в момент выборки из памяти байты меняются местами, так как стоит little endian, что с одной стороны логично, но с другой вообще не дает работать...

внимание вопрос есть возможность это как то победить кроме как менять байты местами в памяти? Есть какие то способы выбирать из памяти данные 16 битными словами, но сохраняя порядок? Пробовали как советовали для STM обращаться к регистру и чарами и 16 битными указателями, и чего только не пробовали, все одно...
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 14 2013, 12:33
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



А зачем? Если для ускорения, то гораздо лучше использовать DMA+SSP
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Sep 14 2013, 17:42
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



так там и есть ДМА, он 16 битными словами пихает приходящий по езернет буфер, в итоге порядок байт меняется местами. Попытка зарядить дма на биг ендиан режим, поменяло местами 16 битные слова)...

перешли на 8 битный режим, но 16 битный быстрее.

там 2 SPI на самом деле, один на DMA, другой ручками, на обоих та же фигня.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Sep 15 2013, 10:33
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Я тоже пробовал (и на 1768 и на 1758), тоже не удалось запустить в 16бит.
Пробовал делать перепаковку с помощью разных ширин источника и приёмника (DMACCxControl::SWidth/...DWidth) - тоже не получилось.
В результате забил - сделал 8-битную пересылку.

PS: Если найдёте решение - стукните мне тоже, пожалуйста
Go to the top of the page
 
+Quote Post
Golikov A.
сообщение Sep 15 2013, 11:45
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454



похоже решения нет, только свапануть начальные массивы...
или передавать начальный массив не чарами а интами, ну то есть опять же свапануть начальный массив...
Go to the top of the page
 
+Quote Post
bseyur
сообщение Sep 16 2013, 08:36
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 65
Регистрация: 8-01-07
Из: Томск
Пользователь №: 24 208



Цитата(Golikov A. @ Sep 14 2013, 03:08) *
Бились сегодня с подобной проблемой на LPC1678, думаю механизмы SPI схожи
если в регистр SPI пихать данные типа так
SPI -> DR = 0x00FF, уходит верно

а если иметь char buf[10];
short int *buf_p =(short int *)buf;
SPI -> DR = buf_p++;
то уходит в поменянном порядке байт, то есть в момент выборки из памяти байты меняются местами, так как стоит little endian, что с одной стороны логично, но с другой вообще не дает работать...

А в вашем случае бит "MSB first" установлен?
Никогда не сталкивался с такими трудностями на NXP, всегда пользовался примитивнейшими конструкциями. Может, дело в объявлении регистров? У меня регистр DR объявлен с типом unsigned long.
Чтение 16-битного слова: data_ptr[index++] = (unsigned short)SSP1DR&0xFFFF;
Запись еще проще, что-то вроде: unsigned int value = 0xAABB; SSP1DR = value;
Передача идет старшим битом вперед, не помню, чтобы использовал обратный порядок. Соответственно, первым при передаче идет старший байт.
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th June 2025 - 09:03
Рейтинг@Mail.ru


Страница сгенерированна за 0.01501 секунд с 7
ELECTRONIX ©2004-2016