1.前言
近日在研究如何提高LCD的刷新率,修改程序发现LCD屏幕用的是模拟通信,所以刷新特别慢,在设置硬件SPI,并使用通过HAL库HAL_SPI_Transmit()函数发送数据后刷新率并没有提升,为此疑惑了很久。
对此研究了整整一两天,网上找到可能的答案并在此记录一下,网上说可能是“HAL库的HAL_SPI_Transmit()函数其实并没有这么快,建议改一下寄存器的方法试一试”,也许是HAL库并不完善,之后我尝试该方法,发现刷新率瞬间提高了。
设备芯片用的是STM32F103C8T6,目的仅仅是通过SPI发送数据给LCD屏幕,而手册规定STM32的SPI时钟最快是18MHz。对于STM32F103的SPI1接口时钟,由72M的PCLK2分频得到,所以分配系数大于等于4(72M/4 = 18M)。对于STM32F103的SPI2/3接口时钟,由36M的PCLK1分频得到,所以分配系数等于2时候达到最高速率,可以更快,但可能不稳定。
由于实验只是LCD刷屏或者实时显示大字体文字刷新速率提高,没有CS片选信号,只需要两条线SCK和SDA。代码基于HAL库开发,只是在SPI配置上直接用寄存器写,小知识在正点原子的库函数和HAL库中写寄存器操作都是通用的,因为无论是标准库还是库函数都是实现对寄存器操作的封装。
2.SPI协议概括
SPI协议简单介绍一下。SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线。
3.SPI相关代码
spi.h
SDA连接PB15 SCK连接PB13(端口选择要根据自己的原理图修改,寻找支持SPI的引脚)
#ifndef __SPI_H#define __SPI_H#include "sys.h"/* SPI接口定义 */#define SPI_INTERFACESPI2#define SPI_PRESCALERSPI_BAUDRATEPRESCALER_2#define SPI_CLK_ENABLE() do{ __HAL_RCC_SPI2_CLK_ENABLE(); }while(0)/* 引脚定义 */#define SPI_SCK_GPIO_PORTGPIOB#define SPI_SCK_GPIO_PIN GPIO_PIN_13#define SPI_SCK_GPIO_CLK_ENABLE()do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)#define SPI_SDA_GPIO_PORTGPIOB#define SPI_SDA_GPIO_PIN GPIO_PIN_15#define SPI_SDA_GPIO_CLK_ENABLE()do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)/* 操作函数 */void SPI2_Init(void); void SPI2_SetSpeed(u8 SpeedSet);u8SPI2_WriteData(u8 *TxData, u16 size);#endif
spi.c
该文件在SPI的GPIO设置初始化中运用了HAL库,其他设置用寄存器,特别是函数编写部分。
#include "spi.h"/** * @brief SPI接口初始化 * @param 无 * @retval无 */void SPI2_Init(void){GPIO_InitTypeDef SPI2_Init_Struct;/* 使能时钟 */SPI_CLK_ENABLE();SPI_SCK_GPIO_CLK_ENABLE();SPI_SDA_GPIO_CLK_ENABLE();/* 初始化SCK引脚 */SPI2_Init_Struct.Pin= SPI_SCK_GPIO_PIN;SPI2_Init_Struct.Mode = GPIO_MODE_AF_PP;SPI2_Init_Struct.Pull = GPIO_PULLUP;SPI2_Init_Struct.Speed= GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &SPI2_Init_Struct);/* 初始化SDA引脚 */SPI2_Init_Struct.Pin= SPI_SDA_GPIO_PIN;SPI2_Init_Struct.Mode = GPIO_MODE_AF_PP;SPI2_Init_Struct.Pull = GPIO_PULLUP;SPI2_Init_Struct.Speed= GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(SPI_SDA_GPIO_PORT, &SPI2_Init_Struct); RCC->APB1RSTR|=1<APB1RSTR&=~(1<CR1|=0<CR1|=1<CR1|=1<CR1|=1<CR1|=0<CR1|=1<CR1|=1<CR1|=7<CR1|=0<CR1|=1<I2SCFGR&=~(1<CR1&=0XFFC7; SPI2->CR1|=SpeedSet<CR1|=1<<6; //SPI设备使能} u8SPI2_WriteData(u8 *TxData, u16 size){u16 i=0;for(i=0;iDR=TxData[i];while((SPI2->SR&1<<1)==0);//等待发送区空 }return 1;}
接下来就是LCD部分代码,其实只要更改两个函数即可,因为其他写文字刷屏的函数都是基于这两个函数写数据的。
//写寄存器函数(命令)void LCD_WR_REG(u8 cmd){ LCD_RS=0;//RS=0的时候写命令SPI2_WriteData(&cmd,sizeof(cmd));}//写LCD数据void LCD_WR_DATA(u8 data){LCD_RS=1;//RS=1的时候写数据 SPI2_WriteData(&data,siezof(data));}
这些函数都是基于正点原子的历程修改的,硬件SPI加上后LCD刷屏速率应该会加快很多,其实可以更快就是加上DMA取数据。