В этой части мы протестируем драйвер SPI с простым интерфейсом, показанным на рисунке ниже. Стандартная периферийная библиотека определяет три устройства SPI (SPI1, SPI2 и SPI3) и 8 возможных часовых делителей тактовых импульсов в stm32f10x_spi.h — для 24-мегагерцовой части платы, делитель на 8 приводит к тактовой частоте SPI 3 МГц (24/8). Мы используем делители на 64, 8 и 2 для медленной, средней и быстрой скорости соответственно. Этот интерфейс обеспечивает инициализацию любого из этих трех устройств с относительно общей конфигурацией. Существует одна операция передачи данных, которая позволяет обмениваться буферами данных с устройством SPI. Технически, каждая передача данных является двунаправленной, но многие устройства не используют эту возможность. Таким образом, операции чтения / записи принимают нулевые указатели для буфера отправки или приема. Устройство SPI также поддерживает 16-битную передачу, поэтому наш интерфейс обеспечивает 16-битную функцию чтения / записи. Наконец, интерфейс позволяет оперативно изменять скорость передачи. Такое изменение может быть необходимо, если на шине есть два ведомых устройства с разными скоростями.
enum spiSpeed { SPI_SLOW , SPI_MEDIUM , SPI_FAST };
void spiInit(SPI_TypeDef* SPIx);
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t *rbuf,
const uint8_t *tbuf, int cnt,
enum spiSpeed speed);
int spiReadWrite16(SPI_TypeDef* SPIx, uint16_t *rbuf,
const uint16_t *tbuf, int cnt,
enum spiSpeed speed);
Инициализация для модуля SPI следует той же общей последовательности, необходимой для любого периферийного устройства —
- Включение тактирования для периферийных и связанных портов GPIO.
- Настройка контактов GPIO.
- Настройка устройства.
Конфигурация необходимых контактов показана выше. Не показаны выводы, доступные для аппаратного управления линией выбора ведомого, потому что мы реализуем это с помощью программного управления. Процесс инициализации показан в ниже. Нас интересует только один режим работы — STM32, действующий как мастер. В более сложных системах может потребоваться значительно изменить эту процедуру, например, передав SPI_InitStructure. Процедура инициализации следует последовательности, показанной ранее, и легко расширяется для поддержки дополнительных периферийных устройств SPI.
static const uint16_t speeds[] = {
[SPI_SLOW] = SPI_BaudRatePrescaler_64 ,
[SPI_MEDIUM] = SPI_BaudRatePrescaler_8 ,
[SPI_FAST] = SPI_BaudRatePrescaler_2};
void spiInit(SPI_TypeDef *SPIx)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
SPI_StructInit(&SPI_InitStructure);
if (SPIx == SPI2) {
/* Enable clocks , configure pins
...
*/
} else {
return;
}
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = speeds[SPI_SLOW];
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);
SPI_Cmd(SPIx, ENABLE);
}
Наш базовый модуль SPI поддерживает отдельные типы транзакций — чтение / запись, что показано ниже. Процедура чтения / записи перебирает количество байтов данных, подлежащих обмену. Каждая итерация состоит из отправки байта, ожидания завершения получателем и получения байта. В случае подпрограммы только для записи внутренний буфер используется для перехвата и отбрасывания принятого байта. Точно так же подпрограмма только для чтения передает последовательность 0xff (эффективно бездействующих) байтов при получении. 16-разрядные подпрограммы работают путем временного изменения конфигурации для поддержки 16-разрядных передач с использованием следующих системных вызовов:
SPI_DataSizeConfig(SPIx, SPI_DataSize_16b); SPI_DataSizeConfig(SPIx, SPI_DataSize_8b);
SPI Read/Write:
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t *rbuf,
const uint8_t *tbuf, int cnt, enum spiSpeed speed)
{
int i;
SPIx->CR1 = (SPIx->CR1 & ~SPI_BaudRatePrescaler_256) |
speeds[speed];
for (i = 0; i < cnt; i++){
if (tbuf) {
SPI_I2S_SendData(SPIx, *tbuf++);
} else {
SPI_I2S_SendData(SPIx, 0xff);
}
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
if (rbuf) {
*rbuf++ = SPI_I2S_ReceiveData(SPIx);
} else {
SPI_I2S_ReceiveData(SPIx);
}
}
return i;
}
SPI Loopback Test :
uint8_t txbuf[4], rxbuf[4];
uint16_t txbuf16[4], rxbuf16[4];
void main()
{
int i, j;
csInit(); // Initialize chip select PC03
spiInit(SPI2);
for (i = 0; i < 8; i++) {
for (j = 0; j < 4; j++)
txbuf[j] = i*4 + j;
GPIO_WriteBit(GPIOC , GPIO_Pin_3 , 0);
spiReadWrite(SPI2, rxbuf , txbuf , 4, SPI_SLOW);
GPIO_WriteBit(GPIOC , GPIO_Pin_3 , 1);
for (j = 0; j < 4; j++)
if (rxbuf[j] != txbuf[j])
assert_failed(__FILE__ , __LINE__);
}
for (i = 0; i < 8; i++) {
for (j = 0; j < 4; j++)
txbuf16[j] = i*4 + j + (i << 8);
GPIO_WriteBit(GPIOC , GPIO_Pin_3 , 0);
spiReadWrite16(SPI2, rxbuf16 , txbuf16 , 4, SPI_SLOW);
GPIO_WriteBit(GPIOC , GPIO_Pin_3 , 1);
for (j = 0; j < 4; j++)
if (rxbuf16[j] != txbuf16[j])
assert_failed(__FILE__ , __LINE__);
}
}
