在嵌入式开发中,SPI通信分为硬件SPI和软件模拟SPI两种方式。硬件SPI依赖MCU内置的SPI外设,而软件SPI则是通过GPIO引脚模拟SPI时序。选择软件SPI通常基于以下考虑:硬件SPI引脚被其他功能占用、需要更灵活的时序控制,或者项目对通信速率要求不高。
LCD9648是一款常见的单色液晶模块,其特点包括:
软件SPI的核心是时序模拟,需要严格遵循以下关键参数:
实测中发现,LCD9648对时序要求相对宽松,但需特别注意:
使用STM32CubeMX创建工程时,关键配置步骤如下:
在Pinout视图中确认使用的GPIO引脚:
时钟配置保持默认即可,软件SPI对时钟精度要求不高
生成代码后,在main.h中会自动生成引脚定义:
c复制#define SCL_Pin GPIO_PIN_13
#define SCL_GPIO_Port GPIOB
/* 其他引脚定义... */
需要手动添加的宏定义(方便代码移植):
c复制#define SCL_Set() HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_SET)
#define SCL_Reset() HAL_GPIO_WritePin(SCL_GPIO_Port, SCL_Pin, GPIO_PIN_RESET)
/* 其他引脚操作宏... */
常见问题排查:
原始代码通常基于标准库开发,移植到HAL库需要注意:
c复制// 原代码可能使用delay_ms(1);
// HAL库替换为:
#define delay_ms HAL_Delay
c复制// 原代码:GPIO_SetBits(GPIOB, GPIO_Pin_13);
// 替换为:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);
c复制void lcd9648_spi_write_byte(uint8_t dat) {
uint8_t i;
for(i=0; i<8; i++) {
if((dat & 0x80) != 0)
SDA_Set();
else
SDA_Reset();
dat <<= 1;
SCL_Reset();
delay_us(1); // 增加微小延时确保稳定
SCL_Set();
delay_us(1);
}
}
c复制uint8_t gdata_buf[96][6]; // 96列×6页(实际使用中根据需要调整)
基础显示功能实现:
c复制void lcd9648_clear(void) {
memset(gdata_buf, 0, sizeof(gdata_buf)); // 使用内存操作加速
lcd_reflash_gram(); // 更新到实际屏幕
}
c复制void lcd_show_num(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode) {
uint8_t t, temp;
uint8_t enshow = 0;
for(t=0; t<len; t++) {
temp = (num / lcd_pow(10, len-t-1)) % 10;
if(enshow==0 && t<(len-1)) {
if(temp==0) {
if(mode&0X80) lcd_show_char(x+(size/2)*t, y, '0', size, mode&0X01);
else lcd_show_char(x+(size/2)*t, y, ' ', size, mode&0X01);
continue;
} else enshow=1;
}
lcd_show_char(x+(size/2)*t, y, temp+'0', size, mode&0X01);
}
}
c复制// 先定义缓冲区
uint8_t display_buf[20];
float a = 6.66;
// 使用sprintf转换
sprintf((char *)display_buf, "roll:%.2f", a);
lcd_show_string(0, 30, LCD_WIDTH, LCD_HEIGHT, 16, display_buf);
性能优化建议:
硬件连接检查清单:
软件调试方法:
典型问题解决方案:
问题:显示内容上下颠倒
解决:修改扫描方向命令(0xC0正常,0xC8反向)
问题:显示对比度异常
解决:调整0x81命令后的参数(0x00-0x3F)
问题:屏幕只有部分区域显示
解决:检查GRAM刷新范围是否正确
移植到其他STM32型号的注意事项:
c复制typedef struct {
char *text;
void (*action)(void);
struct MenuItem *children;
uint8_t itemCount;
} MenuItem;
MenuItem mainMenu[] = {
{"System Info", showSystemInfo, NULL, 0},
{"Settings", NULL, settingsMenu, 3},
// ...
};
图形绘制优化:
低功耗优化策略:
触摸功能集成:
对于带触摸的LCD模块,建议:
分层设计:
使用硬件抽象:
c复制typedef struct {
void (*write_cmd)(uint8_t);
void (*write_data)(uint8_t);
void (*delay_ms)(uint16_t);
} LCD_Interface;
内存优化技巧:
实时性保障:
在实际项目中,我发现将显示刷新与业务逻辑解耦非常重要。可以采用双缓冲机制:一个缓冲用于绘制,另一个用于显示,通过原子操作切换指针。这种方式在STM32F4系列上实测可将刷新效率提升40%以上。