摘要
本应用笔记以驱动SPI接口的OLED显示屏为例,说明了如何添加SPI设备驱动框架及底层硬件驱动,使用SPI设备驱动接口开发应用程序。并给出了在正点原子STM32F4探索者开发板上验证的代码示例。
本文的目的和结构
本文的目的和背景
串行外设接口(Serial Peripheral Interface Bus,SPI),是一种用于短程通信的同步串行通信接口规范,主要应用于单片机系统中。SPI主要应用在 EEPROM、FLASH、实时时钟、AD转换器、数字信号处理器和数字信号解码器等。在芯片的管脚上占用四根线或三根线,简单易用,因此越来越多的芯片集成了这种通信接口。
为了方便应用层程序开发,RT-Thread中引入了SPI设备驱动框架。本文说明了如何使用RT-Thread SPI设备驱动。
本文的结构
本文首先简要介绍了RT-Thread SPI设备驱动框架,然后在正点原子STM32F4探索者开发板上运行了SPI设备驱动示例代码。最后详细描述SPI设备驱动框架接口的使用方法及参数取值。
SPI设备驱动框架简介
RT-Thread SPI设备驱动框架把MCU的SPI硬件控制器虚拟成SPI总线(SPI BUS#n),总线上可以挂很多SPI设备(SPI BUS#0 CSm),每个SPI设备只能挂载到一个SPI总线上。目前,RT-Thread已经实现了很多通用SPI设备的驱动,比如SD卡、各种系列Flash存储器、ENC28J60以太网模块等。SPI设备驱动框架的层次结构如下图所示。

基于前面的介绍用户已经大致了解了RT-Thread SPI设备驱动框架,那么用户如何使用SPI设备驱动框架呢?
运行示例代码
本章节基于正点原子探索者STM32F4 开发板及SPI示例代码,给出了RT-Thread SPI设备驱动框架的使用方法。
示例代码软硬件资源
RT-Thread 源码
ENV工具
SPI设备驱动示例代码
1.5寸彩色OLED显示屏(SSD1351控制器)
MDK5
正点原子探索者STM32F4 开发板的MCU是STM32F407ZGT6,本示例使用USB转串口(USART1)发送数据及供电,使用SEGGER J-LINK连接JTAG调试,STM32F4 有多个硬件SPI控制器,本例使用 SPI1。彩色OLED显示屏板载SSD1351控制器,分辨率128*128。
STM32F4 与 OLED 显示屏管脚连接如下表所示:

SPI设备驱动示例代码包括app.c、drv_ssd1351.c、drv_ssd1351.h
3个文件,drv_ssd1351.c
是OLED显示屏驱动文件,此驱动文件包含了SPI设备ssd1351的初始化、挂载到系统及通过命令控制OLED显示的操作方法。由于RT-Thread上层应用API的通用性,因此这些代码不局限于具体的硬件平台,用户可以轻松将它移植到其它平台上。
配置工程
使用menuconfig配置工程:在env工具命令行使用cd 命令进入 rt-thread/bsp/stm32f4xx-HAL 目录,然后输入menuconfig
命令进入配置界面。

修改工程芯片型号:修改 Device type为STM32F407ZG。
配置shell使用串口1:选中Using UART1,进入RT-Thread Kernel —-> Kernel Device Object菜单,修改the device name for console为uart1。
开启SPI总线及设备驱动并注册SPI总线到系统:进入RT-Thread Components —-> Device Drivers菜单,选中Using SPI Bus/Device device drivers,RT-Thread Configuration界面会默认选中Using SPI1,spi1总线设备会注册到操作系统。
开启GPIO驱动:进入RT-Thread Components —-> Device Drivers菜单,选中Using generic GPIO device drivers。OLED屏需要2个额外的GPIO用于DC、RES信号,SPI总线驱动也需要对片选管脚进行操作,都需要调用系统的GPIO驱动接口。
生成新工程及修改调试选项:退出menuconfig配置界面并保存配置,在ENV命令行输入scons —target=mdk5 -s
命令生成mdk5工程,新工程名为project。使用MDK5打开工程,修改调试选项为J-LINK。

使用list_device命令查看SPI总线:添加SPI底层硬件驱动无误后,在终端PuTTY(打开对应端口,波特率配置为115200)使用list_device
命令就能看到SPI总线。同样可以看到我们使用的UART设备和PIN设备。

添加示例代码
将SPI设备驱动示例代码里的app.c
拷贝到/rt-thread/bsp/stm32f4xx-HAL/applications
目录。drv_ssd1351.c、drv_ssd1351.h
拷贝到/rt-thread/bsp/stm32f4xx-HAL/drivers
目录,并将它们添加到工程中对应分组。如图所示:

在main.c
中调用app_init()
,app_init()
会创建一个oled线程,线程会循环展示彩虹颜色图案和正方形颜图案。

main.c
调用测试代码源码如下:


SPI设备驱动接口使用详解
按照前文的步骤,相信读者能很快的将RT-Thread SPI设备驱动运行起来,那么如何使用SPI设备驱动接口开发应用程序呢?
RT-Thread SPI设备驱动使用流程大致如下:
- 定义SPI设备对象,调用rt_spi_bus_attach_device()挂载SPI设备到SPI总线。
- 调用rt_spi_configure()配置SPI总线模式。
- 使用rt_spi_send()等相关数据传输接口传输数据。
接下来本章节将详细讲解示例代码使用到的主要的SPI设备驱动接口。
挂载SPI设备到总线
用户定义了SPI设备对象后就可以调用此函数挂载SPI设备到SPI总线。
函数原型 :


函数返回 :成功返回RT_EOK,否则返回错误码。
此函数用于挂载一个SPI设备到指定的SPI总线,向内核注册SPI设备,并将user_data保存到SPI设备device里。
注意
- 用户首先需要定义好SPI设备对象device
- 推荐SPI总线命名原则为spix, SPI设备命名原则为spixy,如 本示例的spi10 表示挂载在在 spi1 总线上的 0 号设备。
- SPI总线名称可以在msh shell输入list_device 命令查看,确定SPI设备要挂载的SPI总线。
- user_data一般为SPI设备的CS引脚指针,进行数据传输时SPI控制器会操作此引脚进行片选。
本文示例代码底层驱动drv_ssd1351.c
中 rt_hw_ssd1351_config()`挂载ssd1351设备到SPI总线源码如下:

配置SPI模式
挂载SPI设备到SPI总线后,为满足不同设备的时钟、数据宽度等要求,通常需要配置SPI模式、频率参数。
SPI从设备的模式决定主设备的模式,所以SPI主设备的模式必须和从设备一样两者才能正常通讯。
函数原型 :


函数返回 :返回RT_EOK。
此函数会保存cfg指向的模式参数到device里,当device调用数据传输函数时都会使用此配置信息。
struct rt_spi_configuration 原型如下:

模式/mode :使用spi.h
中的宏定义,包含MSB/LSB、主从模式、 时序模式等,可取宏组合如下。

数据宽度/data_width :根据SPI主设备及SPI从设备可发送及接收的数据宽度格式设置为8位、16位或者32位。
最大频率/max_hz :设置数据传输的波特率,同样根据SPI主设备及SPI从设备工作的波特率范围设置。
注意
挂载SPI设备到SPI总线后必须使用此函数配置SPI设备的传输参数。
本文示例代码底层驱动drv_ssd1351.c
中rt_hw_ssd1351_config()`配置SPI传输参数源码如下:

数据传输
SPI设备挂载到SPI总线并配置好相关SPI传输参数后就可以调用RT-Thread提供的一系列SPI设备驱动数据传输函数。
rt_spi_transfer_message()
函数原型 :


函数返回 : 成功发送返回RT_NULL,否则返回指向剩余未发送的message
此函数可以传输一连串消息,用户可以很灵活的设置message结构体各参数的数值,从而可以很方便的控制数据传输方式。
struct rt_spi_message原型如下:

SPI是一种全双工的通信总线,发送一字节数据的同时会接收一字节数据,参数length为传输一次数据时发送或接收的数据字节数,发送的数据为send_buf指向的缓冲区数据,接收到的数据保存在recv_buf指向的缓冲区。若忽视接收的数据则recv_buf值为NULL,若忽视发送的数据只接收数据,则send_buf值为NULL。
参数next是指向继续发送的下一条消息的指针,若只发送一条消息,则此指针值置为NULL。
rt_spi_send()
函数原型 :


函数返回 : 成功发送的数据字节数
调用此函数发送send_buf指向的缓冲区的数据,忽略接收到的数据。
此函数等同于调用rt_spi_transfer_message()
传输一条消息,message参数配置如下:

注意
调用此函数将发送一次数据。开始发送数据时片选开始,函数返回时片选结束。
本文示例代码底层驱动drv_ssd1351.c
调用rt_spi_send()
向SSD1351发送指令和数据的函数源码如下:

rt_spi_send_then_send()
函数原型 :


函数返回 : 成功返回RT_EOK,否则返回错误码
此函数可以连续发送2个缓冲区的数据,忽略接收到的数据。发送send_buf1时片选开始,发送完send_buf2后片选结束。
此函数等同于调用rt_spi_transfer_message()
传输2条消息,message参数配置如下:

rt_spi_send_then_recv()
函数原型 :

函数返回 : 成功返回RT_EOK,否则返回错误码
此函数发送第一条消息send_buf时开始片选,此时忽略接收到的数据,然后发送第二条消息,此时发送的数据为空,接收到的数据保存在recv_buf里,函数返回时片选结束。
此函数等同于调用rt_spi_transfer_message()
传输2条消息,message参数配置如下:

rt_spi_sendrecv8()
和rt_spi_sendrecv16()
函数是对此函数的封装,rt_spi_sendrecv8()
发送一个字节数据同时收到一个字节数据,rt_spi_sendrecv16()
发送2个字节数据同时收到2个字节数据。
SPI设备驱动应用
本文示例使用SSD1351显示图像信息,首先需要确定信息在显示器上的行列起始地址,调用ssd1351_write_cmd()
向SSD1351发送指令,调用ssd1351_write_data()
向SSD1351发送数据,源代码如下:

原作者:RT-Thread应用笔记