最近做项目需要一块大显示屏作为机器人的face,在tb上看了看选购了这块:
官方给的例程算是比较全面,奈何我们用到的核心板——沁恒CH32V307(逐飞科技)——比较“小众”,其实官方给出了CH32F1、F2系列的例程,但因为我们用的是seekfree的库,通过Mounriver Studio开发,所以需要自己手动移植。
一、移植效果
目前我已经移植成功了,如下:
4.0英寸SPI串口TFT液晶显示屏
首先我们随便找个官方给的demo(可以去官方下载),从中把lcd.c/.h,FONT.h,GUI.c/.h,test.c/.h添加到Mounriver的工程中,然后会出现一堆错误,原因主要出在SPI的库不同,所以我们第一步先修改SPI库,使二者兼容。
在此之前,我先说明一下我的引脚分配:
//================电源接线==============================================================//
// LCD模块 CH32单片机
// VCC 接 DC5V/3.3V //电源
// GND 接 GND //电源地
//=============液晶屏数据线接线==========================================================//
// SDI(MOSI) 接 A7 //液晶屏SPI总线数据写信号
// SDO(MISO) 接 //液晶屏SPI总线数据读信号,如果不需要读可以不接线
//=============液晶屏控制线接线==========================================================//
// LED 接 D0 //液晶屏背光控制信号(如果不需要控制可以不接)
// SCK 接 A5 //液晶屏SPI总线时钟信号
// LCD_RS 接 D7 //液晶屏数据/命令控制信号
// LCD_RST 接 B7 //液晶屏复位控制信号
// LCD_CS 接 D4 //液晶屏片选控制信号
//===============触摸屏触接线===========================================================//
//如果模块不带触摸功能, 或者带有触摸功能但是不需要触摸功能,则不需要进行触摸屏接线
// CTP_INT 接 B4 //电容触摸屏触摸中断信号
// CTP_SDA 接 B5 //电容触摸屏IIC总线数据信号
// CTP_RST 接 B6 //电容触摸屏触摸复位信号
// CTP_SCL 接 B3 //电容触摸屏IIC总线时钟信号
二、修改SPI文件
在官方写的SPI.h中可以发现只有三个函数:
u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte);
void SPI1_Init(void);
void SPI_SetSpeed(SPI_TypeDef* SPIx,u8 SpeedSet);
这三个函数的调用主要发生在lcd.h中,我们进入SPI.c阅读下他们的意思,便可以根据zf的库重写/替换一下这三个函数。
第一个函数 u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte) 的意思是用SPIx写一个字节的数据,返回值为SPIx->DR,它可以用zf的void spi_write_8bit (spi_index_enum spi_n, uint8_t data)平替,但需要注意的是后者是没有返回值的,所以我们要重写一下这个函数:
u8 SPI_WriteByte(spi_index_enum spi_n, uint8_t data)
{
uint8_t spi_write_8bit (spi_n,data);
return ((SPI_TypeDef *)(spi_index[spi_n]))->DATAR;
}
这样做的好处是,由于lcd.c中调用了很多官方库中的spi函数,我们直接以官方库函数的名称命名,就省得去lcd.c中把所有的 u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte) 换成 void spi_write_8bit (spi_index_enum spi_n, uint8_t data) 了。(但还是要改实参,也没省啥事)
第二个函数 void SPI1_Init(void) 是初始化硬件SPI,用zf库中的 void spi_init (spi_index_enum spi_n, spi_mode_enum mode, uint32_t baud, spi_pin_enum sck_pin, spi_pin_enum mosi_pin, spi_pin_enum miso_pin, gpio_pin_enum cs_pin) 平替就可以。
我们需要把lcd.c中的 void LCD_Init(void) 函数中的 SPI1_Init(); 改成 spi_init(SPI_1, 0, 10*1000*1000, SPI1_SCK_A5, SPI1_MOSI_A7, SPI1_MISO_A6, D4) ; 其中的实参需要根据你自己的引脚分配进行修改,模式、速率也可以自己修改。(实践发现,第三个参数——波特率,严重影响lcd刷屏速率,有条件的话可以尽量调快点。)
第三个函数 void SPI_SetSpeed(SPI_TypeDef* SPIx,u8 SpeedSet) 是更改SPI速度的函数,进入到内部可以发现,若SpeedSet传入为1则进行2分频,其他则为8分频。但找遍了zf的SPI库,并没有发现与之相似的函数,那我们就自己来重写一下吧:
/*****************************************************************************
* @name :void SPI_SetSpeed(spi_index_enum SPIx, uint8_t SpeedSet)
* @date :2024-05-08
* @function :设置spi速度
* @parameters :
* @retvalue :None
******************************************************************************/
void SPI_SetSpeed(spi_index_enum SPIx, uint8_t SpeedSet)
{
uint32_t spi_baudrate_div; // SPI波特率分频值
// 根据SpeedSet的值选择分频
if(SpeedSet == 1)
{
spi_baudrate_div = SPI_BaudRatePrescaler_2; // 二分频
}
else
{
spi_baudrate_div = SPI_BaudRatePrescaler_8; // 八分频
}
// 根据SPIx选择对应的SPI模块并设置速度
switch(SPIx)
{
case SPI_1:
// 设置SPI1的速度
SPI1->CTLR1 |= spi_baudrate_div; // 设置新的分频值
break;
case SPI_2:
// 设置SPI2的速度
SPI2->CTLR1 |= spi_baudrate_div; // 设置新的分频值
break;
// 如果有更多的SPI模块,可以继续添加case
default:
// 如果传入了不支持的SPIx,可以添加错误处理
break;
}
}
使用效果与官方提供的是一样的。
三、修改lcd文件
对于lcd的修改主要在 .h 文件中。对端口宏定义的修改(我是根据我的引脚分配来的):
#define LED D0 //背光控制引脚
#define LCD_CS D4 //片选引脚
#define LCD_RS D7 //寄存器/数据选择引脚
#define LCD_RST B7 //复位引脚
对IO定义直接操作寄存器,快速IO操作:
//GPIO置位(拉高)
#define LCD_CS_SET ((GPIO_TypeDef*)gpio_group[(LCD_CS>>5)])->BSHR = (uint16_t)(1 << (LCD_CS & 0x0F)) //片选端口
#define LCD_RS_SET ((GPIO_TypeDef*)gpio_group[(LCD_RS>>5)])->BSHR = (uint16_t)(1 << (LCD_RS & 0x0F)) //数据/命令
#define LCD_RST_SET ((GPIO_TypeDef*)gpio_group[(LCD_RST>>5)])->BSHR = (uint16_t)(1 << (LCD_RST & 0x0F)) //复位
//GPIO复位(拉低)
#define LCD_CS_CLR ((GPIO_TypeDef*)gpio_group[(LCD_CS>>5)])->BCR = (uint16_t)(1 << (LCD_CS & 0x0F)) //片选端口
#define LCD_RS_CLR ((GPIO_TypeDef*)gpio_group[(LCD_RS>>5)])->BCR = (uint16_t)(1 << (LCD_RS & 0x0F)) //数据/命令
#define LCD_RST_CLR ((GPIO_TypeDef*)gpio_group[(LCD_RST>>5)])->BCR = (uint16_t)(1 << (LCD_RST & 0x0F)) //复位
比较费事的是官方用了一个这样的操作:
#define LCD_LED PBout(LED) //LCD背光
一层一层往底倒会发现:
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
这是位带操作,实现51类似的GPIO控制功能。但是查阅资料发现,相较于ARM内核,RISC-V内核并没有位带操作,所以我们来自己模拟一下:
/**************自定义位带操作***********/
typedef struct { //根据芯片内存大小端设置,当前为小端模式,大端反过来
uint32_t bit0 :1;
uint32_t bit1 :1;
uint32_t bit2 :1;
uint32_t bit3 :1;
uint32_t bit4 :1;
uint32_t bit5 :1;
uint32_t bit6 :1;
uint32_t bit7 :1;
uint32_t bit8 :1;
uint32_t bit9 :1;
uint32_t bit10 :1;
uint32_t bit11 :1;
uint32_t bit12 :1;
uint32_t bit13 :1;
uint32_t bit14 :1;
uint32_t bit15 :1;
uint32_t bit16 :1;
uint32_t bit17 :1;
uint32_t bit18 :1;
uint32_t bit19 :1;
uint32_t bit20 :1;
uint32_t bit21 :1;
uint32_t bit22 :1;
uint32_t bit23 :1;
uint32_t bit24 :1;
uint32_t bit25 :1;
uint32_t bit26 :1;
uint32_t bit27 :1;
uint32_t bit28 :1;
uint32_t bit29 :1;
uint32_t bit30 :1;
uint32_t bit31 :1;
} GPIO_REG;
#define PDout(n) (((GPIO_REG *)(&(GPIOD->OUTDR)))->bit##n)
#define PDin(n) (((GPIO_REG *)(&(GPIOD->INDR)))->bit##n)
#define LCD_LED PDout(0) //LCD背光
至此lcd.h文件就差不多修改完了,接下来修改lcd.c文件,涉及到的改动较少。
第一,把所有SPI函数的实参SPI1改成SPI_1,把所有的 delay_ms() 改成 system_delay_ms()。
第二 ,修改一下lcd的gpio初始化函数:
void LCD_GPIOInit(void)
{
gpio_init(D0, GPO, 0, GPIO_PIN_CONFIG);
gpio_init(D4, GPO, 0, GPIO_PIN_CONFIG);
gpio_init(D7, GPO, 0, GPIO_PIN_CONFIG);
gpio_init(B7, GPO, 0, GPIO_PIN_CONFIG);
LCD_LED=1; //点亮背光
}
在这里你不需要开启RCC时钟,也不需要定义结构体,直接无脑调用 gpio_init() 就完事儿了。
至此,修改工作基本完成,别忘了把包含的头文件改成 #include "zf_common_headfile.h" ,还有GUI和test文件里的一些小细节(如延时函数什么的)。
至于电容触摸屏的修改,其与文章所述过程类似,需要此功能的小伙伴仿照着自行修改吧。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yipenmian/article/details/138726946
|
|