Добрался наконец и до SPI. Есть задумка разобраться с радиомодулями NRF24 без всяких библиотек и желательно с какой нибудь STM32. Может и карты памяти как нибудь поковыряю. Поэтому начнем с малого, с настройки и проверки интерфейса.
Как работает SPI было расписано в этой статье. Мастер выдает такты, слейв читает данные по каждому такту, в общем то и все. Из вкусностей имеется DMA, но об этом в другой раз. Сейчас интерес представляет только настройка.
В качестве примера, я взял две отладочные платки Discovery (stm32f407vg) и Nucleo (stm32f103rb), для меня это самые ходовые серии и у них чуток отличается инициализация.
Дискавери будет мастером, и по очереди будет слать по 2 байта (0xfef0 и 0xf0f0), нуклео будет их принимать, если на входе 0xfef0, то опустить ногу PC0, если любой другой байт, то поднять. Таким образом, если данные правильно передаются, то на тестовом пине PC0 будет меандр. Да обратите внимание, у STM32 можно передавать сразу по 16 бит.
Оба исходника выковыряны из примеров, идущих вместе с SPL. Отправляются данные с небольшой задержкой, принимаются в прерывании. Названия прерываний внутри stm32f4xx.h и stm32f10x.h.
И там, и там задействован SPI2. Чип селект(CS) не использовался.
Стоит быть внимательным, что ноги нужно настраивать на вход и выход руками, т.е. не забыть что для мастера нога sck это выход, то для слейва это вход. Аналогично и остальные ноги.
Для мастера
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_usart.h" #include "stm32f4xx_spi.h" int main(void) { //включаем тактирование RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); ///======= PB14 cs OUT ///======= PC2 MISO INPUT ///======= PB15 MOSI OUT ///======= PB13 SCK OUT //настраиваем ноги GPIO_InitTypeDef spi_pin_setup; spi_pin_setup.GPIO_Mode = GPIO_Mode_AF; spi_pin_setup.GPIO_OType = GPIO_OType_PP; spi_pin_setup.GPIO_Speed = GPIO_Speed_50MHz; spi_pin_setup.GPIO_PuPd = GPIO_PuPd_DOWN; spi_pin_setup.GPIO_Pin = GPIO_Pin_2; GPIO_Init(GPIOC, &spi_pin_setup); spi_pin_setup.GPIO_Pin = GPIO_Pin_15; GPIO_Init(GPIOB, &spi_pin_setup); spi_pin_setup.GPIO_Pin = GPIO_Pin_13; GPIO_Init(GPIOB, &spi_pin_setup); GPIO_PinAFConfig(GPIOC, GPIO_PinSource2, GPIO_AF_SPI2); GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2); GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2); //настраиваем Spi SPI_InitTypeDef spi_setup; SPI_I2S_DeInit(SPI2); spi_setup.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi_setup.SPI_Mode = SPI_Mode_Master; spi_setup.SPI_DataSize = SPI_DataSize_16b; spi_setup.SPI_CPOL = SPI_CPOL_Low; spi_setup.SPI_CPHA = SPI_CPHA_1Edge; spi_setup.SPI_NSS = SPI_NSS_Soft; spi_setup.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; spi_setup.SPI_FirstBit = SPI_FirstBit_MSB; spi_setup.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &spi_setup); SPI_Cmd(SPI2, ENABLE); while(1) { //отправляем данные SPI_I2S_SendData(SPI2, 0xfef0); while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); for(volatile uint16_t i=0; i < 1000; i++); SPI_I2S_SendData(SPI2, 0xf0f0); for(volatile uint16_t i=0; i < 1000; i++); while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET); } } |
Для слейва
#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_spi.h" void SPI2_IRQHandler (void) //обработчик прерывания spi2 { if (SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==SET) //если прерывание по приему { if(SPI_I2S_ReceiveData(SPI2) != 0xfef0) //читаем данные и сравниваем с 0xfef0 { GPIO_ResetBits(GPIOC, GPIO_Pin_0); //если fef0 то опускаем ногу } else { GPIO_SetBits(GPIOC, GPIO_Pin_0); //если нет то поднимаем } } } int main(void) { __enable_irq(); //включаем тактирование RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //инициализируем ноги GPIO_InitTypeDef miso; GPIO_InitTypeDef sck; GPIO_InitTypeDef mosi; GPIO_InitTypeDef cs; GPIO_InitTypeDef tst_pin; tst_pin.GPIO_Mode = GPIO_Mode_Out_PP; tst_pin.GPIO_Pin = GPIO_Pin_0; tst_pin.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOC, &tst_pin); miso.GPIO_Mode = GPIO_Mode_AF_PP; miso.GPIO_Speed = GPIO_Speed_50MHz; miso.GPIO_Pin = GPIO_Pin_14; mosi.GPIO_Mode = GPIO_Mode_IN_FLOATING; mosi.GPIO_Speed = GPIO_Speed_50MHz; mosi.GPIO_Pin = GPIO_Pin_15; cs.GPIO_Mode = GPIO_Mode_IN_FLOATING; cs.GPIO_Speed = GPIO_Speed_50MHz; cs.GPIO_Pin = GPIO_Pin_1; sck.GPIO_Mode = GPIO_Mode_IN_FLOATING; sck.GPIO_Speed = GPIO_Speed_50MHz; sck.GPIO_Pin = GPIO_Pin_13; GPIO_Init(GPIOB, &sck); GPIO_Init(GPIOB, &cs); GPIO_Init(GPIOB, &mosi); GPIO_Init(GPIOB, &miso); //настраиваем spi SPI_InitTypeDef spi_setup; spi_setup.SPI_Direction = SPI_Direction_2Lines_FullDuplex; spi_setup.SPI_Mode = SPI_Mode_Slave; spi_setup.SPI_DataSize = SPI_DataSize_16b; spi_setup.SPI_CPOL = SPI_CPOL_Low; spi_setup.SPI_CPHA = SPI_CPHA_1Edge; spi_setup.SPI_NSS = SPI_NSS_Soft; spi_setup.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; spi_setup.SPI_FirstBit = SPI_FirstBit_MSB; spi_setup.SPI_CRCPolynomial = 7; SPI_Init(SPI2, &spi_setup); SPI_Cmd(SPI2, ENABLE); //включить spi2 //настраиваем прерывание от SPI NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = SPI2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); NVIC_EnableIRQ(SPI2_IRQn); //настраиваем прерывания от SPI2 SPI_I2S_ITConfig(SPI2, SPI_I2S_IT_RXNE, ENABLE); //разрешаем прерывания по приему spi2 while(1); } |
Проект собран под кейл, однако должен работать и в кокосе.
Подскажите вывод NSS равносильно SS в AVR, то есть для выбора ведомого устройства? Если да, то можно ли вместо него использовать любой вывод МК настроенный на выход?
Спасибо.
можно
Может я что-то пропустил, скажите что означает строка
spi_setup.SPI_CRCPolynomial = 7; где «7» это что?
полином для расчета контрольной суммы