SPI 驱动
更新时间:2017-08-08 阅读:2934
前言
SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。
- SDO – 主设备数据输出,从设备数据输入;
- SDI – 主设备数据输入,从设备数据输出;
- SCLK – 时钟信号,由主设备产生;
- CS – 从设备使能信号,由主设备控制
其中SDO/SDI/SCLK三根线实现了全双工的通信,虽然简单高效了很多,但是由于器件的寻址只有一个CS片选脚来指定,当连接多台SPI设备时,会显得比较麻烦。
SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿/下降沿时改变,在紧接着的下降沿/上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟的改变(上沿和下沿为一次),就可以完成8位数据的传输。
初始化
一般裸驱开发过程为系统初始化-->GPIO配置-->各驱动模块初始化-->主循环实现功能。第一步系统初始化是每一个例程所必须的,初始化的过程也是相同的,所以只需调用系统初始化接口即可。
下面的三个部分都是需要我们根据自己的需求自行实现,在上一小节我们已经学习了如何配置GPIO,接下来就是各个模块的初始化。本例程只介绍SPI的初始化。
初始化函数
SPI初始化的函数原型为:
void spi_init(QN_SPI_TypeDef * SPI, uint32_t bitrate, enum SPI_BUFFER_WIDTH width, enum SPI_MODE mode)
传入参数:
- SPI通道
- SPI波特率
- SPI传输数据宽度
- SPI工作模式
初始化示例
spi_init(QN_SPI1, SPI_BITRATE(200000), SPI_8BIT, SPI_MASTER_MOD);
SPI通道
QN9021中只含有SPI1通道,所以第一个参数必须为QN_SPI1。
SPI波特率
SPI 波特率范围在 62500~4000000 之间。在宏定义中可看到波特率设定的方法。
/// SPI bit rate algorithm #define SPI_BITRATE(x) ((__USART_CLK/(2*(x)) - 1) << SPI_POS_CLK_DIV_MASTER)
宏定义中,x 影响到的是 SPI_POS_CLK_DIV_MASTER 的值,根据名称,这个值应该是一个分频系数.
追踪 SPI_POS_CLK_DIV_MASTER ,发现 SPI_POS_CLK_DIV_MASTER 的宏定义为16。
#define SPI_POS_CLK_DIV_MASTER 16
很明显 (__USART_CLK/(2*(x)) - 1) 这个值应该是要写入某个寄存器,在寻址相关寄存器的掩码中发现如下介绍。
/* SPI REGISTER MASK */ // CR0 #define SPI_MASK_BITRATE 0x003F0000 /* 21 - 16 */
原来寄存器的16位到21位存放的就是分频系数。在回到之前的宏定义,假设 __USART_CLK/(2*(x)) - 1 = div ,那么可发现其实就是对 __USART_CLK 的 2*(div+1)倍分频。 div 去取值范围为 0~63 ,查得 __USART_CLK 为 8MHz 的频率,故波特率取值为 62.5KHz~4Mhz , x 的单位为 Hz 。
#define CLK_8M (8000000UL) /*!< Clock is 8MHz */ #define __USART_CLK CLK_8M /*!< UART and SPI clock frequency */
SPI读写
接下来程序初始化了rxbuffer,作为数据收发缓存使用。
for (j = 0; j < 40; j++) { rxbuffer[j] = 0; buffer[j] = j; }
设置写入标志位,然后向SPI总线上写入buffer数组中的前4个字节数据,完成写入后清除标志位。
//Write 4byte data tx_flag = 1; // spi_write(QN_SPI0, buffer, 4, led_blink_right); spi_write(QN_SPI1, buffer, 4, led_blink_right); while(tx_flag);
设置读取标志位,从SPI总线上读取4个字节然后存入rxbuffer数组中,完成后清除标志位。
//Read 4byte data rx_flag = 1; // spi_read(QN_SPI0, rxbuffer, 4, led_blink_left); spi_read(QN_SPI1, rxbuffer, 4, led_blink_left); while(rx_flag);