1. STM32 SPI
1.1 STM32的SPI接口
SPI可以设置为主、从两种模式,并且支持全双工模式,而配置为主、从模式或软件、硬件NSS,在操作上有很大的区别。由于一个项目需求,笔者对STM32的硬件模式和主从模式进行了一些研究,走了很多弯路,也查询了很多资料,现在终于调通了,因此写一篇文章记录调试心得,以及很多需要注意的地方。
本文主要探讨主模式和从模式NSS硬件和软件管理。 2. SPI Master 初始化及测试 2.1 硬件NSS模式
以下是初始化代码
/************打开时钟*************/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //SPI_MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// SPI_Cmd(SPI1, ENABLE); //先不打开SPI
SPI_SSOutputCmd(SPI1, ENABLE); //SPI的NSS引脚控制开启
}
SPI配置为主模式,采用硬件NSS有几点需要注意,若采用硬件NSS,一定要把NSS引脚输出设置为GPIO_Mode_AF_PP,否则程序无法正确控制。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
①NSS引脚修改为GPIO_Mode_Out_PP /************引脚配置*************/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //SPI_NSS GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_4); int main() {
u8 SPI_TX=10,SPI_RX=0; //
SPI1_Configuration(); //spi初始化
while(1)
{
SPI_Cmd(SPI1,ENABLE); //启动SPI
/**************向从设备发送一个字节*************/
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1,SPI_TX);
/**************保存将接收到的数据*************/
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET);
SPI_RX = SPI_I2S_ReceiveData(SPI1);
SPI_Cmd(SPI1,DISABLE); //关闭SPI
SPI_TX++; //发送的值+1
delay_100ms(10); //延时1秒
if(SPI_TX>50) {SPI_TX=10;}
}
}
**效果测试** 上图是刚刚的代码运行的效果,用逻辑分析仪抓取的SPI波形。 从设备设置的是收到什么就会返回什么,由于SPI的特性是在发送的同时接收数据,且从设备不能主动向主设备发送数据,因此在发送新数据的时候才能收到上此次发送的旧数据。2.3.2 软件NSS模式while(1) { GPIO_ResetBits(GPIOA,GPIO_Pin_4); //拉低CS /**************向从设备发送一个字节*************/ while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1,SPI_TX); GPIO_SetBits(GPIOA,GPIO_Pin_4); //拉高CS **效果测试**
软件NSS模式下,SPI是常开的,读写数据的时候需要人为控制CS引脚 可以看到CS线在最后一个CLK脉冲发送完成后就立即拉高了,和硬件模式有一点区别
如果去掉这两个函数会怎样呢?
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) == RESET);while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE) == RESET); while(1) {
GPIO_ResetBits(GPIOA,GPIO_Pin_4); //拉低CS
/**************向从设备发送一个字节*************/
SPI_I2S_SendData(SPI1,SPI_TX);
delay_us(2); //延时2微秒
GPIO_SetBits(GPIOA,GPIO_Pin_4); //拉高CS
**效果测试** SPI-NSS-SOFT-TX2 可以看到,程序似乎没有按照预想顺序进行,延时函数好像没有在SPI_I2S_ReceiveData()结束后才执行。这也是使用硬件SPI常犯的错误,因为在使用硬件SPI时,外设在进行数据读写的时候是不占用内核时间的,内核把数据丢给外设寄存器就完事了,内核只需要等待外设返回结束标志位,刚才屏蔽掉的两个函数就是在等待外设返回结束标志,而这个等待时间其实也可以做很多事情。经过测试,内核对外设的操作只用了大概0.4uS。
|