STM32F103软件模拟IIC驱动0.96寸OLED:从零搭建与界面交互优化
1. 硬件准备与接线指南拿到STM32F103核心板和0.96寸OLED模块时我第一反应是翻看引脚定义。这块4针OLED通常采用IIC接口接线其实特别简单只需要4根线VCC、GND、SCL、SDA。但要注意供电电压我刚开始用5V供电结果屏幕发烫后来才发现这类OLED多数是3.3V供电的。具体接线时建议用杜邦线按这个方案连接OLED_VCC → 开发板3.3VOLED_GND → 开发板GNDOLED_SCL → 任意GPIO如PB6OLED_SDA → 任意GPIO如PB7这里有个坑要注意软件模拟IIC不限制引脚但硬件IIC有固定引脚。我推荐用PB6/PB7这样以后切到硬件IIC也方便。接线完成后建议用万用表测下通断有次我就因为杜邦线接触不良折腾了半天。2. 软件模拟IIC协议实现2.1 IIC时序模拟关键点模拟IIC最核心的就是时序控制这里我总结出三个关键时间参数起始信号SCL高电平时SDA从高到低跳变停止信号SCL高电平时SDA从低到高跳变数据有效性SCL上升沿采样数据用GPIO模拟的代码框架如下// 定义GPIO操作宏 #define OLED_SCL_H() GPIO_SetBits(GPIOB, GPIO_Pin_6) #define OLED_SCL_L() GPIO_ResetBits(GPIOB, GPIO_Pin_6) #define OLED_SDA_H() GPIO_SetBits(GPIOB, GPIO_Pin_7) #define OLED_SDA_L() GPIO_ResetBits(GPIOB, GPIO_Pin_7) void IIC_Start() { OLED_SDA_H(); OLED_SCL_H(); delay_us(5); OLED_SDA_L(); delay_us(5); OLED_SCL_L(); }2.2 典型问题排查调试时我用逻辑分析仪抓波形发现两个常见问题时序过快STM32F103主频72MHz直接操作GPIO太快需要加delay_us(5)延时应答异常从设备无响应时建议在IIC_Wait_Ack()里加超时判断我设置的300ms超时3. OLED驱动库开发3.1 显示基础功能封装构建驱动库时我采用分层设计底层IIC读写函数Write_IIC_Command/Data中间层基本绘图画点、线、清屏应用层字符、汉字、图片显示显示字符的函数实现很有讲究void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 size) { u8 c chr - ; // ASCII码偏移 if(size 16) { OLED_Set_Pos(x, y); for(u8 i0; i8; i) OLED_WR_Byte(F8X16[c*16i], OLED_DATA); OLED_Set_Pos(x, y1); for(u8 i0; i8; i) OLED_WR_Byte(F8X16[c*16i8], OLED_DATA); } else { /* 8x6字体处理 */ } }3.2 汉字显示方案显示汉字需要字库支持我试过三种方案直接烧录字库到Flash占用空间大外挂SPI Flash存储字库成本高只提取需要的汉字推荐第三种方案最实用用PCtoLCD2005工具生成特定汉字的点阵数据const u8 Hzk[][16] { {0x00,0x40,0x42,0xCC,0x00,0x00,0x00,0xFC,0x04,0x04,0xFC,0x00,0x00,0x00,0x00,0x00}, //中,0 {0x10,0x10,0xD0,0x3F,0x10,0x10,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} //文,1 };4. 界面交互优化实战4.1 多页面管理系统实现按键翻页时我设计了一个状态机模型typedef enum { PAGE_HOME, PAGE_SETTING, PAGE_INFO } PageType; PageType currentPage PAGE_HOME; void Page_Switch(PageType newPage) { OLED_Clear(); switch(newPage) { case PAGE_HOME: OLED_ShowString(0, 0, Main Menu, 16); break; case PAGE_SETTING: OLED_ShowString(0, 0, Settings, 16); break; } currentPage newPage; }4.2 按键消抖处理按键检测我用的是状态机方式比简单延时更可靠#define KEY_DEBOUNCE_TIME 20 u8 KEY_Scan(u8 mode) { static u8 key_up 1; static u16 debounce_cnt 0; if(mode) key_up 1; if(key_up (KEY00 || KEY10 || WK_UP1)) { debounce_cnt; if(debounce_cnt KEY_DEBOUNCE_TIME) { key_up 0; debounce_cnt 0; if(WK_UP1) return WKUP_PRES; else if(KEY00) return KEY0_PRES; else return KEY1_PRES; } } else if(KEY01 KEY11 WK_UP0) { key_up 1; debounce_cnt 0; } return 0; }5. 性能优化技巧5.1 局部刷新机制全屏刷新会导致闪烁我实现了区域刷新函数void OLED_Refresh_Area(u8 x1, u8 y1, u8 x2, u8 y2) { for(u8 yy1; yy2; y) { OLED_Set_Pos(x1, y); for(u8 xx1; xx2; x) { OLED_WR_Byte(framebuffer[y][x], OLED_DATA); } } }5.2 帧缓冲设计建立显存数组能大幅提升性能u8 framebuffer[8][128]; // 对应OLED的8页x128列 void OLED_Update() { for(u8 p0; p8; p) { OLED_Set_Pos(0, p); for(u8 c0; c128; c) { OLED_WR_Byte(framebuffer[p][c], OLED_DATA); } } }6. 项目进阶方向6.1 动画效果实现通过定时器中断实现流畅动画void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) ! RESET) { static u8 pos 0; OLED_DrawBMP(pos, 0, 32, 4, anim_frames[frame_index]); pos (pos 1) % 96; TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } }6.2 菜单系统优化采用结构体数组定义菜单项更灵活typedef struct { u8 text[16]; void (*action)(void); } MenuItem; MenuItem mainMenu[] { {System Info, ShowSystemInfo}, {Settings, EnterSettings}, {Calibration, RunCalibration} };
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2603485.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!