SPI
,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线
,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如MSP430单片机系列处理器。
SPI
总线包括4条逻辑线
缺点:
没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。
采用同步方式(Synchronous)传输数据
Master 设备会根据将要交换的数据来产生相应的时钟脉冲(Clock Pulse),时钟脉冲组成了时钟信号(Clock Signal) ,时钟信号通过时钟极性 (CPOL) 和 时钟相位 (CPHA) 控制着两个 SPI 设备间何时数据交换以及何时对接收到的数据进行采样,来保证数据在两个设备之间是同步传输的。
采用主-从模式(Master-Slave)的控制方式
SPI 规定了两个 SPI 设备之间通信必须由主设备 (Master) 来控制次设备 (Slave)。 一个 Master 设备可以通过提供 Clock 以及对 Slave 设备进行片选 (Slave Select) 来控制多个 Slave 设备,SPI 协议还规定 Slave 设备的 Clock 由 Master 设备通过 SCK 管脚提供给 Slave 设备, Slave 设备本身不能产生或控制 Clock,没有 Clock 则 Slave 设备不能正常工作。
数据交换(Data Exchanges)
SPI 设备间的数据传输之所以又被称为数据交换,是因为 SPI 协议规定一个 SPI 设备不能在数据通信过程中仅仅只充当一个 “发送者(Transmitter)” 或者 “接收者(Receiver)”。在每个 Clock 周期内,SPI 设备都会发送并接收一个 bit 大小的数据(不管主设备好还是从设备),相当于该设备有一个 bit 大小的数据被交换了。一个 Slave 设备要想能够接收到 Master 发过来的控制信号,必须在此之前能够被 Master 设备进行访问 (Access)。所以,Master 设备必须首先通过 SS/CS pin 对 Slave 设备进行片选, 把想要访问的 Slave 设备选上。 在数据传输的过程中,每次接收到的数据必须在下一次数据传输之前被采样。如果之前接收到的数据没有被读取,那么这些已经接收完成的数据将有可能会被丢弃,导致 SPI 物理模块最终失效。因此,在程序中一般都会在 SPI 传输完数据后,去读取 SPI 设备里的数据, 即使这些数据(Dummy Data)在我们的程序里是无用的(虽然发送后紧接着的读取是无意义的,但仍然需要从寄存器中读出来)。
本次学习用到的是四线spi的oled屏幕来显示,学习SPI协议。
引脚 | 引脚定义 |
---|---|
GND | 地 |
VCC | 电源,3.3~5v |
D0 | 4 线 ISP 接口模式:时钟线(CLK) |
D1 | 4 线 ISP 接口模式:串行数据线(MOSI) |
RES | 4 线 ISP 接口模式:命令/数据标志位(RET复位) |
DC | 命令/数据标志位 |
CS | OLED 片选 |
单片机液晶显示模块都是用点阵的方式来显示,显示汉字字符的时候就会用到字模,字模就是汉字符号在液晶屏幕上显示的时候对应的编码,如果是我们自己来的话就十分麻烦,我们可以用字模获取软件,这样就十分的方便,把获取的字模拿来用就行。
软件下载连接: http://www.lcdwiki.com/zh/0.96inch_SPI_OLED_Module
可以去这里自行下载。
void OLED_Init_GPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能B端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15; //GPIOB10,11,12,13,15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIOB10、11、12、13、15
GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_15);
}
void OLED_Init(void)
{
OLED_Init_GPIO(); //初始化GPIO
delay_ms(200);
OLED_Reset(); //复位OLED
/**************初始化SSD1306*****************/
OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/
OLED_WR_Byte(0x00,OLED_CMD); /*set lower column address*/
OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/
OLED_WR_Byte(0x40,OLED_CMD); /*set display start line*/
OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/
OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/
OLED_WR_Byte(0xFF,OLED_CMD); /*128*/
OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/
OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/
OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/
OLED_WR_Byte(0x3F,OLED_CMD); /*duty = 1/64*/
OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/
OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/
OLED_WR_Byte(0XF1,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD); /*set vcomh*/
OLED_WR_Byte(0x30,OLED_CMD);
OLED_WR_Byte(0x8D,OLED_CMD); /*set charge pump disable*/
OLED_WR_Byte(0x14,OLED_CMD);
OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
}
这是基于我的上一个博客,对温湿度传感器采集回的数据在oled上的一个显示,这里就不再介绍温湿度的采集了,感兴趣的可以去看我的上一个博客。这里着重讲讲对于怎样再oled回显温度和湿度。
void TEST_MainPage(u32 Tem,u32 Hum)//这里传进来的Tem为温度,Hum为湿度
{
GUI_ShowCHinese(10,0,16,"温度",1);
GUI_ShowString(40,0,":",16,1);
GUI_ShowCHinese1(10,20,16,"湿度",1);
GUI_ShowString(40,20,":",16,1);
GUI_ShowNum(50,0,(u32 *)(Tem/10),2,16,1);
GUI_ShowString(68,0,".",16,1);
GUI_ShowNum(75,0,(u32 *)(Tem%10),1,16,1);
GUI_ShowNum(50,20,(u32 *)(Hum/10),2,16,1);
GUI_ShowString(68,20,".",16,1);
GUI_ShowNum(75,20,(u32 *)(Hum%10),1,16,1);
GUI_ShowString(85,0,"*c",16,1);
GUI_ShowString(85,20,"%",16,1);
delay_ms(1500);
delay_ms(1500);
}
关于文字的滚动用到的是厂家给的硬件刷屏的程序,并不是自己写的模拟的程序,在实际测试过程中会发现自己写的模拟的不是很流畅,很卡,甚至有时候还会丢字。所以建议还是用厂家给的硬件刷屏程序,这样会十分流畅。
int main(void)
{
delay_init();
NVIC_Configuration();
OLED_Init();
OLED_Clear(0);
OLED_WR_Byte(0x2E,OLED_CMD);
OLED_WR_Byte(0x26,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x07,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xFF,OLED_CMD);
TEST_MainPage(0,0);
OLED_WR_Byte(0x2F,OLED_CMD);
while(1);
}
在实际学习过程中通过看厂家给的源码,就是厂家已经写好的oled显示的库我还是学到了很多,虽然很多都不是自己写的,但是通过阅读他们的代码,不仅是在代码风格还是原理实现上对我的帮组都十分的大。在学习过程中也遇到了许许多多的问题,比如oled无法显示字符等等,经过后续排查发现都是自己不够细心而导致的,都不是什么大问题,但是对spi也只是初步的了解,还不是特别熟悉,还需后续继续努力!