|
STM32F3 SPI, Непонятное поведение |
|
|
|
Jul 1 2013, 11:10
|
Участник

Группа: Участник
Сообщений: 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 бит) Если кто-то сталкивался с подобным поведением, подскажите, в каком направлении двигаться?  Заполнение полей конфигурации для каждого устройства: Код 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; }
|
|
|
|
|
Jul 1 2013, 11:52
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(bseyur @ Jul 1 2013, 15:10)  Если кто-то сталкивался с подобным поведением, подскажите, в каком направлении двигаться?  Многое зависит от того каким образом вы записываете данные в регистр 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.
|
|
|
|
|
Jul 1 2013, 12:09
|
Участник

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

|
Благодарю за ответ! Завтра попробую.
|
|
|
|
|
Jul 1 2013, 12:42
|

Профессионал
    
Группа: Участник
Сообщений: 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-ти битном режиме.
|
|
|
|
|
Jul 1 2013, 12:45
|
Гуру
     
Группа: Свой
Сообщений: 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 зависит от того, как к ним обращаться))
|
|
|
|
|
Jul 1 2013, 13:12
|
Участник

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

|
Цитата(Genadi Zawidowski @ Jul 1 2013, 19:42)  .... Просто передавайте data в 16-ти битном режиме. Изначально я так и делал. Только в 16-битной передаче контроллер почему-то меняет местами младший и старший байты, т.е. таким образом: D7-D0, D15-D8. Но должна идти старшим битом вперед! Соответственно пришлось поменять байты вручную. Я до сих пор не понимаю, почему так!
|
|
|
|
|
Jul 2 2013, 05:08
|
Участник

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

|
Цитата(adnega @ Jul 1 2013, 18:52)  Многое зависит от того каким образом вы записываете данные в регистр DR. ..... Действительно, как только я начал обращаться к регистру DR как к байту, все глюки ушли. Благодарю за наводку.  Порядок байт теперь также верный. По правде говоря, сначала я посчитал эту версию несостоятельной, к примеру, чипы 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?
|
|
|
|
|
Sep 13 2013, 20:08
|
Гуру
     
Группа: Свой
Сообщений: 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 битными указателями, и чего только не пробовали, все одно...
|
|
|
|
|
Sep 16 2013, 08:36
|
Участник

Группа: Участник
Сообщений: 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; Передача идет старшим битом вперед, не помню, чтобы использовал обратный порядок. Соответственно, первым при передаче идет старший байт.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|