【前言】
感谢极术社区、灵动微电子,给予了我这次试用Mini-F5375-OB开发板评测活动的机会。本篇我将分享如何体验从模拟SPI到硬件SPI来驱动ST7789LCD屏的体验。
【硬件】
1、Mini-F5375-OB开发板
2、ST7789LCD屏,为四线驱动接口。
3、逻辑分析仪
4、示波器
【硬件连线】
本次试验,我使用开发板的SP1与LCD屏进行连接,接线如下:
LCD_PWR:PA0
LCD_RST:PA2
LCD_WR: PA3
LCD_CS: PA4
LCD_SCK PA5
LCD_SDA PA7
为了方便测试,我使用了一个面包板进行转接,连接好后图如下:
(线有点多---)
【驱动过程】
1、为了验证驱动,我先使用模拟的SPI进行测试。先在LCD.h中宏定义如下:
#define LCD_PWR_PORT GPIOA
#define LCD_PWR_PIN GPIO_Pin_0
#define LCD_RST_PORT GPIOA
#define LCD_RST_PIN GPIO_Pin_2
#define LCD_WR_PORT GPIOA
#define LCD_WR_PIN GPIO_Pin_3
#define LCD_CS_PORT GPIOA
#define LCD_CS_PIN GPIO_Pin_4
#define LCD_SCK_PORT GPIOA
#define LCD_SCK_PIN GPIO_Pin_5
#define LCD_SDA_PORT GPIOA
#define LCD_SDA_PIN GPIO_Pin_7
#define LCD_PWR(n) (n ? GPIO_SetBits(LCD_PWR_PORT, LCD_PWR_PIN) : GPIO_ResetBits(LCD_PWR_PORT, LCD_PWR_PIN))
#define LCD_RST(n) (n? GPIO_SetBits(LCD_RST_PORT, LCD_RST_PIN) : GPIO_ResetBits(LCD_RST_PORT, LCD_RST_PIN))
#define LCD_WR(n) (n? GPIO_SetBits(LCD_WR_PORT, LCD_WR_PIN) : GPIO_ResetBits(LCD_WR_PORT, LCD_WR_PIN))
#define LCD_CS(n) (n? GPIO_SetBits(LCD_CS_PORT, LCD_CS_PIN) : GPIO_ResetBits(LCD_CS_PORT, LCD_CS_PIN))
#define LCD_SCK(n) (n? GPIO_SetBits(LCD_SCK_PORT, LCD_SCK_PIN) : GPIO_ResetBits(LCD_SCK_PORT, LCD_SCK_PIN))
#define LCD_SDA(n) (n? GPIO_SetBits(LCD_SDA_PORT, LCD_SDA_PIN) : GPIO_ResetBits(LCD_SDA_PORT, LCD_SDA_PIN))
2、初始化GPIO,由于不用读取LCD屏的参数,所以配置IO为高速上拉输出即可:
/**************************************************************
函数名称 : lcd_gpio_init
函数功能 : lcd gpio初始化
输入参数 : 无
返回值 : 无
备注 : 无
**************************************************************/
void lcd_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
/*Configure Powr */
GPIO_InitStruct.GPIO_Pin = LCD_PWR_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_PWR_PORT, &GPIO_InitStruct);
/*config rst pin*/
GPIO_InitStruct.GPIO_Pin = LCD_RST_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_RST_PORT, &GPIO_InitStruct);
/*config cs pin*/
GPIO_InitStruct.GPIO_Pin = LCD_CS_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_CS_PORT, &GPIO_InitStruct);
/*config wr pin*/
GPIO_InitStruct.GPIO_Pin = LCD_WR_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_WR_PORT, &GPIO_InitStruct);
/*Configure sck pin */
GPIO_InitStruct.GPIO_Pin = LCD_SCK_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_SCK_PORT, &GPIO_InitStruct);
/*Configure sda pin */
GPIO_InitStruct.GPIO_Pin = LCD_SDA_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(LCD_SDA_PORT, &GPIO_InitStruct);
}
3、定义好后,编写向总线写入指令与数据的函数:
/**************************************************************
函数名称 : lcd_write_reg
函数功能 : 向lcd驱动芯片写一个命令
输入参数 : reg:要写入的命令
返回值 : 无
备注 : LCD_WR为0时表示写命令
**************************************************************/
void lcd_write_reg(uint8_t reg)
{
uint8_t i;
LCD_CS(0);
LCD_WR(0);
for (i = 0; i < 8; i++)
{
LCD_SCK(0);
if (reg & 0x80)
LCD_SDA(1);
else
LCD_SDA(0);
LCD_SCK(1);
reg <<= 1;
}
LCD_CS(1);
LCD_WR(1);
}
/**************************************************************
函数名称 : lcd_write_reg
函数功能 : 向lcd驱动芯片写一个数据
输入参数 : data:要写入的数据
返回值 : 无
备注 : LCD_WR为1时表示写数据
**************************************************************/
void lcd_write_data(uint8_t data)
{
uint8_t i;
LCD_CS(0);
LCD_WR(1);
for (i = 0; i < 8; i++)
{
LCD_SCK(0);
if (data & 0x80)
LCD_SDA(1);
else
LCD_SDA(0);
LCD_SCK(1);
data <<= 1;
}
LCD_CS(1);
LCD_WR(1);
}
4、到此模拟spi的移植就完成,其余功能函数见附件。
【测试】
将lcd.c/h加入mdk工程中,在Main中添加测试函数:
int main(void)
{
PLATFORM_Init();
lcd_init();
lcd_clear(BLUE);
lcd_show_string(30, 100, 200, 30, (uint8_t *)("hello world"), 24, 0);
lcd_show_string(20, 140, 200, 30, (uint8_t *)("Mini-F5375-OB"), 24, 0);
编译下载到开发板,效果如下:
为了查看速度如何,我接上逻辑分析仪查看SCK的时序,发现速度为1.5MHz。
肉眼查看刷新速度非常慢。
【提升速度之一】
编优化选项修改为O3,再查看SCK的时序图,提升到了6.25MHz,提升速度还是非常明显的。
【提升速度之二】
修改GPIO为直接对寄存器的操作代码如下:
#define LCD_PWR(n) (n ? (LCD_PWR_PORT->BSRR = LCD_PWR_PIN) : (LCD_PWR_PORT->BRR = LCD_PWR_PIN))
#define LCD_RST(n) (n? (LCD_RST_PORT->BSRR = LCD_RST_PIN) : (LCD_RST_PORT->BRR = LCD_RST_PIN))
#define LCD_WR(n) (n? (LCD_WR_PORT->BSRR = LCD_WR_PIN) : (LCD_WR_PORT->BRR = LCD_WR_PIN))
#define LCD_CS(n) (n? (LCD_CS_PORT->BSRR = LCD_CS_PIN) : (LCD_CS_PORT->BRR = LCD_CS_PIN))
#define LCD_SCK(n) (n? (LCD_SCK_PORT->BSRR = LCD_SCK_PIN) : (LCD_SCK_PORT->BRR = LCD_SCK_PIN))
#define LCD_SDA(n) (n? (LCD_SDA_PORT->BSRR = LCD_SDA_PIN) : (LCD_SDA_PORT->BRR = LCD_SDA_PIN))
修改后,速度提升1倍,上升到了13.33MHz。刷新明显提升。
【小结】
通过一系列的优化,模拟SPI也可以跑出好成绩了,说明MM32F5375的GPIO外设是非常优秀的。
【硬件SPI驱动】
上面是使用GPIO来模拟SPI,最高速度也就跑到了13.3MHz,从F5370的数据手上册上看,SPI是挂载在APB2时钟总线上,最高时钟可以跑到180MHz,SPI最小分频为2,那莫理论上可以跑到90MHz的速度。
【初始化SPI1】
初始化的配置如下:
/**************************************************************
函数名称 : spi1_init
函数功能 : 硬件sp1初始化
输入参数 : 无
返回值 : 无
备注 : 配置成主机模式
**************************************************************/
static void spi1_init(void)
{
SPI_InitTypeDef SPI_InitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI_StructInit(&SPI_InitStruct);
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_DataWidth = 8;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_RX);
SPI_BiDirectionalLineConfig(SPI1, SPI_Enable_TX);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_5); /* PA4 SPI_NSS */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5); /* PA5 SPI_SCK */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_5); /* PA6 SPI_MISO */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5); /* PA7 SPI_MOSI */
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_StructInit(&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_Cmd(SPI1, ENABLE);
}
修改发送数据函数如下:
/**************************************************************
函数名称 : sp1_write_byte
函数功能 : SPI1 写size个字节
输入参数 : w_data:要写入的字节,size:写入字节大小
返回值 : 0 --> 写入成功,其他 --> 写入失败
备注 : 无
**************************************************************/
uint8_t sp1_write_byte(uint8_t *r_data, uint16_t size)
{
uint8_t i = 0;
volatile uint8_t Data = 0;
for (i = 0; i < size; i++)
{
SPI_SendData(SPI1, r_data[i]);
while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
{
}
while (RESET == SPI_GetFlagStatus(SPI1, SPI_FLAG_RXAVL))
{
}
}
return 0;
}
编译后下载程序到开发板,肉眼观看整屏刷新,相比模拟SPI是提升巨大。当我设置分频为2时,我的逻辑分析仪已经不正确的捕获到SCK的时序图了。我把分频系数调整为8。测得速度为18.20MHz
当分频系数调为2时,测得频率为62.5MHz
【注】我的示波器比较差,升级后才是100M的带宽。可能数据不一定准确。
【总结】
通过上面的测试,我使用模拟以及硬件SPI来驱动LCD屏,同时通过逻辑分析仪、示波器来对SCK的速度进行了简单的测试发现F5375的外设非常优秀。
初充:
我后面打印了一下总线时钟,原来使用示例配置的总线时钟为150MHz,我后面重新配置了一下总线时钟为180MHz,SPI分频为4时,测得SPI的总线时钟为45.4MHz:
修改为2分频时,由于我接上示波器的探头就会有干扰,根据计算那么SPI总线是可以跑到90MHz的。