本文介绍STM8L051F3的SPI相关知识。内容分为以下几部分:
1、SPI简介SPI(Serial peripheral interface):串行外围接口。SPI可以与外部设备进行半/全双工、同步、串行通讯,它可以作为主设备来为外部设备提供通讯时钟,同时还可以配置为多主机模式运行。SPI的主要特点如下:
- 全双工同步传输(3线)
- 在2线或没有双向传输的数据线的情况下的单工同步传输
- 主机或从机运行模式
- 主模式频率(最大fSYSCLK/2)
- 从模式频率(最大fSYSCLK/2)
- 快速通讯最大SPI速度:8MHz
- 在主模式或从模式都可以硬件或软件管理NSS(片选引脚)
- 可编程时钟极性和相位
- 可编程数据顺序是高位或低位先发送
- 在中断功能中有独立的发送和接收标志位
- SPI总线忙状态标志
- 具备中断功能的主模式下故障和溢出标志
- 具备DMA功能(发送和接收请求)的1字节发送和接收缓存器
- 具备可靠通讯的硬件CRC:
- CRC值在发送模式作为最后一个字节发送
- 可对最后接收的字节进行CRC错误检测
- 唤醒功能:
- 只有在全双工或半双工的发送模式下可以将MCU从低功耗模式下唤醒
SPI经过4个引脚连接外部设备:
- MISO:主输入/从输出数据;在主模式用于接收数据,在从模式用于发送数据
- MOSI:主输出/从输入数据;在主模式用于输出数据,在从模式用于接收数据
- SCK:主模式下时钟输出,从模式下时钟输入
- NSS:从机选择;这是一个用于选择从设备的引脚,相当于‘片选’,让主设备与从设备独立通讯,避免数据线的占用。从机的NSS可以由主机的标准IO口输入,SPI进入主机模式失败的状态:MSTR位重置,设备被配置为从模式
从机选择(NSS)引脚管理。通过配置SPI_CR2寄存器的SSM位可以设置使用硬件或软件管理NSS引脚:
- 软件NSS管理(SSM=1):在这个配置下,从机选择的信息是有内部SPI_CR2寄存器SSI为决定的,外部NSS引脚可以用作其他用途
- 硬件NSS管理(SSM=0):作为主机,该设备可以允许多主机功能;作为从机,NSS引脚作为输入,当NSS被拉低时说明从机被选中,否则NSS引脚为高电平
时钟相位与时钟极性。通过CPOL和CPHA位可以选择四种不同的时钟关系。CPOL(时钟极性)位控制在没有数据发送时时钟线的状态值,该位收主机和从机模式影响。如果CPOL位被重置,时钟引脚在空闲时是低电平,如果CPOL位被置位,时钟引脚在空闲状态时高电平。
如果CPHA(时钟相位)位被置位,第二个时钟引脚的边沿(如果CPOL位是重置的就是下降沿,如果CPOL为是置位的就是上升沿)采样最高位,在第一个时钟状态改变的期间数据被锁存。如果CPHA(时钟相位)位被重置,第一个时钟引脚的边沿(如果CPOL位是置位的就是下降沿,如果CPOL为是重置的就是上升沿)采样最高位,在第二个时钟状态改变的期间数据被锁存。如下图:
配置SPI为从机。在从机模式下,时钟线是由主机设备输入时钟信号,由SPI_CR1寄存器的BR[2:0]位设置,不影响数据传输数率。从机模式的配置步骤如下:
1)配置CPOL和CPHA位来定义数据发送与串行时钟四种关系之一,为了数据正确的传输,主机与 从机的配置应该一致
2)数据结构(取决于SPI_CR1寄存器的LSBFIRST位决定的高位或低位先发送)必须与主机一致
3)在硬件模式,NSS引脚在数据传输期间必须保持低电平;在软件模式通过置位和清除SPI_CR2寄 存器的SSI位实现控制
4)清除MSTR位和设置SPE位来指定引脚作为替换功能
配置SPI为主机。在主机模式下,串行时钟在SCK引脚产生。配置步骤如下:
1)配置SPI_CR1的BR[2:0]位来定义串行时钟的波特率
2)配置CPOL和CPHA位来定义数据发送与串行时钟四种关系之一
3)配置SPI_CR1寄存器的LSBFIRST位来定义数据结构
4)在硬件模式下,在完整的数据传输序列中,NSS引脚连接到一个高电平信号;在软件模式下,设 置SPI_CR2的SSM和SSI位。
5)设置MSTR和SPE位
SPI还有单工通讯功能,数据发送和接收过程、CRC校验等等功能的详细介绍可以参考官方手册RM0031第30章。
2、OLED显示2.1 SPI配置本小节介绍OLED的SPI通讯(默认使用技新的7Pin-0.96寸OLED模块)。实现通过STM8L051F3的硬件SPI与OLED通讯,并让OLED显示:OLED TEST。使用的例程:STM8L051F3_13_SPI。这里需要注意的是,与OLED的SPI通讯实际上是只使用了SPI的MOSI、SCK、NSS引脚,而OLED还需要两个(RES与DC)控制引脚(RES与DC引脚在oled.c与oled.h文件中定义和初始化)。
整体的OLED与STM8L051F3核心板的接线为(电源VCC与GND接V与G):D0--->PB5(SCK)、D1--->PB6(MOSI)、RES--->PB0、DC--->PB3、CS--->PB4(NSS)。其中RES与DC的引脚在oled.h和oled.c文件中定义初始化。
SPI(STM8L051F3是在用的是SPI1)的配置步骤如下:
1)使能SPI1外设时钟
2)配置MISO、MOSI、SCK、NSS引脚
3)拉高NSS引脚
4)初始化SPI1:高位先发送、SPI时钟4分频、SPI主模式、空闲相位高、第二个时钟源发送数据、NSS引脚软件控制、CRC校验0x55
5)使能SPI1
SPI1读写一字节步骤:
1)等待SPI1空闲
2)SPI1发送一字节数据
3)等待SPI1接收一字节数据
4)读取SPI1接收的数据
2.2 例程介绍SPI初始化配置以及读写函数实现在spi.c文件下:
void SPI_Config(void)
{
/* 使能SPI1时钟 */ CLK_PeripheralClockConfig(CLK_Peripheral_SPI1, ENABLE); /* 配置MISO引脚为上拉输入 */ GPIO_Init(SPI_MISO_GPIO_PORT, SPI_MISO_GPIO_PINS, GPIO_Mode_In_PU_No_IT); /* 配置MOSI引脚为推挽输出 */ GPIO_Init(SPI_MOSI_GPIO_PORT, SPI_MOSI_GPIO_PINS, GPIO_Mode_Out_PP_High_Fast); /* 配置SCK引脚为推挽输出 */ GPIO_Init(SPI_SCK_GPIO_PORT, SPI_SCK_GPIO_PINS, GPIO_Mode_Out_PP_High_Fast); /* 配置NSS引脚为推挽输出 */ GPIO_Init(SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PINS, GPIO_Mode_Out_PP_High_Fast); /* 拉高NSS引脚 */ SPI_NSS_HIGH(); /* 初始化SPI1,高位先发送, 时钟8分频,SPI主模式,空闲相位高, 第二个时钟沿发送数据,NSS引脚软件控制,CRC校验:0x55 */ SPI_Init(SPI1, SPI_FirstBit_MSB, SPI_BaudRatePrescaler_4, SPI_Mode_Master, SPI_CPOL_High, SPI_CPHA_2Edge, SPI_Direction_2Lines_FullDuplex, SPI_NSS_Soft, 0x07); SPI_Cmd(SPI1, ENABLE); //使能SPI }
uint8_t SPI_RW_Byte(uint8_t data)
{
uint8_t temp; /* 等待SPI1空闲 */ while(SPI_GetFlagStatus(SPI1, SPI_FLAG_TXE)== RESET); /* SPI1发送数据 */ SPI_SendData(SPI1, data); /* 等待接受一字节数据 */ while(SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE)== RESET); /* 读取接收的数据 */ temp = SPI_ReceiveData(SPI1); /* 返回接收的数据 */ return temp; }
spi.h文件中定义SPI1相关引脚:
/* 定义SPI的IO PORT与PIN */
#define SPI_MISO_GPIO_PORT (GPIOB)
#define SPI_MISO_GPIO_PINS (GPIO_Pin_7)
#define SPI_MOSI_GPIO_PORT (GPIOB)
#define SPI_MOSI_GPIO_PINS (GPIO_Pin_6)
#define SPI_SCK_GPIO_PORT (GPIOB)
#define SPI_SCK_GPIO_PINS (GPIO_Pin_5)
#define SPI_NSS_GPIO_PORT (GPIOB)
#define SPI_NSS_GPIO_PINS (GPIO_Pin_4)
#define SPI_NSS_LOW() GPIO_ResetBits(SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PINS)
#define SPI_NSS_HIGH() GPIO_SetBits(SPI_NSS_GPIO_PORT, SPI_NSS_GPIO_PINS)
主函数如下:
void main(void)
{
LED_Init(); //初始化LED SPI_Config(); OLED_Init(); //OLED初始化 OLED_Clear(); //OLED清屏 OLED_ShowString(30,0,"OLED TEST");//OLED显示 OLED TEST while(1) { delay_ms(300); GPIO_ToggleBits(LED1_GPIO_PORT, LED1_GPIO_PINS); //切换LED1状态 } }
使用ST-LINK把程序下载到开发板,LED1闪烁,OLED显示:OLED TEST。效果如下:
|