一、SPI硬件资源介绍
STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。其中 SPI1 是 APB2 上的设备,最高通信速率达 36Mbtis/s,SPI2、SPI3 是 APB1 上的设备,最高通信速率为 18Mbits/s。除了通讯速率,在其它功能上没有差异。其中 SPI3 用到了下载接口的引脚,这几个引脚默认功能是下载,第二功能才是 IO 口,如果想使用 SPI3 接口,则程序上必须先禁用掉这几个 IO 口的下载功能。一般在资源不是十分紧张的情况下,这几个 IO 口是专门用于下载和调试程序,不会复用为 SPI3。
二、配置SPI1
如上图所示,因为stm32F407战舰版的引脚已经连接完成,所以当我们使用cubemx配置SPI时,他会默认选择PA4-PA6复用SPI1,但是我们需要使用PB3-PB5怎么处理
硬件片选和软件片选的区别
所谓硬件片选指的是SPI本身具有片选信号,当我们通过SPI发送数据时,SPI外设自动拉低CS信号使能从机,发送完成后自动拉高CS信号释放从机,这个过程是不需要软件操作的。而软件片选则是需要使用GPIO作为片选信号,SPI在发送数据之前,需要先通过软件设置作为片选信号的GPIO输出低电平,发送完成之后再设置该GPIO输出高电平。一般选择软件片选。
三、SPI函数详解
在stm32f1xx_hal_spi.h头文件中可以看到spi的操作函数。分别对应轮询,中断和DMA三种控制方式。
- 轮询: 最基本的发送接收函数,就是正常的发送数据和接收数据
- 中断: 在SPI发送或者接收完成的时候,会进入SPI回调函数,用户可以编写回调函数,实现设定功能
- DMA: DMA传输SPI数据
一般使用SPI传输用下面两个函数
//发送数据
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
*hspi: 选择SPI1/2,比如&hspi1,&hspi2
*pData : 需要发送的数据,可以为数组
Size: 发送数据的字节数,1 就是发送一个字节数据
Timeout: 超时时间,就是执行发送函数最长的时间,超过该时间自动退出发送函数
//接收数据
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
*hspi: 选择SPI1/2,比如&hspi1,&hspi2
*pData : 接收发送过来的数据的数组
Size: 接收数据的字节数,1 就是接收一个字节数据
Timeout: 超时时间,就是执行接收函数最长的时间,超过该时间自动退出接收函数
四、测试SPI是否配置成功
(1) 自定义一个传输函数,并且在头文件中声明该函数。
uint8_t spi1_read_write_byte(uint8_t txdata)
{
uint8_t rxdata;
HAL_SPI_TransmitReceive(&hspi1, &txdata, &rxdata, 1, 1000);
return rxdata; /* 返回收到的数据 */
}
(2) 配置软件片选线
#define SPI_CS_LOW() HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin,GPIO_PIN_RESET)
#define SPI_CS_HIGH() HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET)
(3)读取flash的ID,看SPI是否配置成功
uint16_t norflash_read_id(void)
{
uint16_t deviceid;
SPI_CS_LOW();
spi1_read_write_byte(0x90); /* 发送读 ID 命令 */
spi1_read_write_byte(0); /* 写入一个字节 */
spi1_read_write_byte(0);
spi1_read_write_byte(0);
deviceid = spi1_read_write_byte(0xFF) << 8; /* 读取高8位字节 */
deviceid |= spi1_read_write_byte(0xFF); /* 读取低8位字节 */
SPI_CS_HIGH();
return deviceid;
}
(4)配置fputc函数,打印flash的ID
#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__*/
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);
return ch;
}
(5)打印结果
SPI_CS_HIGH();//此处必须要释放SPI_CS线,不然直接选中从机低电平信号没反应,比如此代码如果不释放CS线,那么读取到的id为0xffff
uint16_t ret=norflash_read_id();
printf("ID:%x\r\n",ret);
while (1)
{
}