移植好U8g2图形库的STM32F407标准库工程模板,0.96寸OLED驱动程序

news/2024/12/29 17:22:17/

移植好U8g2图形库的STM32F407标准库工程模板,用的0.96寸OLED屏(SSD1306),用硬件IIC驱动。

花了一晚上时间去移植。开发板主控MCU用的是STM32F407VET6,I2C接口用I2C1,SCL接PB6,SDA接PB7。

嵌入式相关文章:https://blog.zeruns.tech/category/IOT/

电子/电路相关文章:https://blog.zeruns.tech/category/electrical/

U8g2简介

U8g2图形库是一个用于嵌入式设备的单色图形库,支持多种单色OLED和LCD显示控制器,如SSD1306,ST7920等。U8g2库可以从Arduino IDE的库管理器安装,也可以移植到STM32等平台。U8g2库支持三种绘图模式:全屏缓存模式,页面缓存模式和U8x8字符模式。U8g2库的使用需要选择合适的构造函数,初始化显示器,设置引脚号,编写回调函数和绘图指令。

U8g2图形库的优点是可以使用多种字体,支持中文显示,提供丰富的图形程序,如线条,框,圆,位图等。U8g2图形库的缺点是需要占用一定的内存空间,速度较慢,不支持无控制器的显示屏。U8g2图形库的应用场景有:显示传感器数据,制作时钟,显示菜单,显示动画等。U8g2图形库是一个功能强大,兼容性好,易于使用的单色图形库。

效果图


关于晶振的问题

我晶振用的8Mhz的,时钟树配置那PLL参数我修改过,使MCU工作主频168Mhz,如果换用其他频率的晶振需要修改参数,具体怎么改自行百度。

修改的地方如下:

stm32f4xx.h 文件的137行。

#define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
// 这里的8000000改成你的晶振频率,单位Hz

system_stm32f4xx.c 文件的364行和394行。

#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F469_479xx)/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */#define PLL_M      4
// 这里4对应下面时钟树图片中的 /M 的 /4
#if defined (STM32F40_41xxx)
#define PLL_N      168
// 这里168对应下面时钟树图片中的 *N 的 x168

元件购买地址

  • 0.96寸OLED屏:https://u.jd.com/1zjpDNC
  • STM32F407开发板:https://s.click.taobao.com/7AtnHFu
  • 杜邦线:https://u.jd.com/1zjFP0Z

代码

完整工程文件下载:https://url.zeruns.tech/JUoKJ 提取码:t6wt

部分代码:

main.c

#include "stm32f4xx.h"
// #include "Timer.h"
#include "Delay.h"
#include "u8g2.h"
#include "OLED.h"
#include "IWDG.h"uint8_t u8x8_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr);
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
void u8g2_Init(u8g2_t *u8g2);
void draw(u8g2_t *u8g2);int main(void)
{uint8_t t = 0;IWDG_Configuration(); // 初始化看门狗OLED_I2C_Init();      // 初始化OLEDu8g2_t u8g2;u8g2_Init(&u8g2); // 初始化U8g2RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 启用GPIOA的外设时钟GPIO_InitTypeDef GPIO_InitStructure;                   // 定义结构体GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;          // 设置GPIO口模式为输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // 设置GPIO口6GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;       // 设置GPIO口速度100MhzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         // 设置GPIO口为推挽输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;       // 设置GPIO上下拉模式GPIO_Init(GPIOA, &GPIO_InitStructure);                 // 初始化GPIODelay_ms(100);u8g2_DrawLine(&u8g2, 0, 0, 127, 63); // 画一条线,起始坐标(0,0),终点坐标(127,63)u8g2_SendBuffer(&u8g2);              // 发送缓冲区数据u8g2_DrawLine(&u8g2, 127, 0, 0, 63);u8g2_SendBuffer(&u8g2);Delay_ms(300);u8g2_ClearBuffer(&u8g2);  //清除缓冲区数据draw(&u8g2);u8g2_SendBuffer(&u8g2);Delay_ms(1000);u8g2_ClearBuffer(&u8g2);IWDG_FeedDog(); // 喂狗,防止CPU复位u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);    //选择字库u8g2_DrawStr(&u8g2, 0, 15, "Hello World!");u8g2_SetFont(&u8g2, u8g2_font_wqy16_t_chinese2);u8g2_DrawUTF8(&u8g2, 0, 30, "H你好世界");u8g2_SetFont(&u8g2, u8g2_font_wqy12_t_chinese2);u8g2_DrawUTF8(&u8g2, 0, 43, "H你好世界");u8g2_SetFont(&u8g2, u8g2_font_fur11_tr);u8g2_DrawUTF8(&u8g2, 0, 59, "blog.zeruns.tech");u8g2_SendBuffer(&u8g2);Delay_ms(1300);while (1){Delay_ms(100);u8g2_ClearBuffer(&u8g2);//清除缓冲区数据if (++t >= 32)t = 1;u8g2_DrawCircle(&u8g2, 64, 32, t, U8G2_DRAW_ALL);   //画圆u8g2_DrawCircle(&u8g2, 32, 32, t, U8G2_DRAW_ALL);u8g2_DrawCircle(&u8g2, 96, 32, t, U8G2_DRAW_ALL);u8g2_SendBuffer(&u8g2); // 发送缓冲区数据GPIO_ToggleBits(GPIOA, GPIO_Pin_6);IWDG_FeedDog(); // 喂狗,防止CPU复位}
}
// https://blog.zeruns.tech
uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{static uint8_t buffer[32]; /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */static uint8_t buf_idx;uint8_t *data;switch (msg){case U8X8_MSG_BYTE_SEND:data = (uint8_t *)arg_ptr;while (arg_int > 0){buffer[buf_idx++] = *data;data++;arg_int--;}break;case U8X8_MSG_BYTE_INIT:/* add your custom code to init i2c subsystem */break;case U8X8_MSG_BYTE_START_TRANSFER:buf_idx = 0;break;case U8X8_MSG_BYTE_END_TRANSFER:HW_I2cWrite(buffer, buf_idx);   //硬件I2C写字节break;default:return 0;}return 1;
}
// https://blog.zeruns.tech
uint8_t u8g2_gpio_and_delay(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
{switch (msg){case U8X8_MSG_GPIO_AND_DELAY_INIT:OLED_I2C_Init();    //初始化break;case U8X8_MSG_DELAY_MILLI:Delay_ms(arg_int);  //延时break;case U8X8_MSG_GPIO_I2C_CLOCK:break;case U8X8_MSG_GPIO_I2C_DATA:break;default:return 0;}return 1; // command processed successfully.
}void u8g2_Init(u8g2_t *u8g2)
{// u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2,软件I2Cu8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8g2_gpio_and_delay); // 初始化 u8g2,硬件I2Cu8g2_InitDisplay(u8g2);                                                                       // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态u8g2_SetPowerSave(u8g2, 0);                                                                   // 打开显示器u8g2_SetContrast(u8g2, 88);                                                                   // 设置屏幕亮度u8g2_ClearBuffer(u8g2);                                                                       // 清除缓冲区
}
// https://blog.vpszj.cn
void draw(u8g2_t *u8g2)
{u8g2_SetFontMode(u8g2, 1);              /*字体模式选择*/u8g2_SetFontDirection(u8g2, 0);         /*字体方向选择*/u8g2_SetFont(u8g2, u8g2_font_inb24_mf); /*字库选择*/u8g2_DrawStr(u8g2, 0, 20, "U");u8g2_SetFontDirection(u8g2, 1);u8g2_SetFont(u8g2, u8g2_font_inb30_mn);u8g2_DrawStr(u8g2, 21, 8, "8");u8g2_SetFontDirection(u8g2, 0);u8g2_SetFont(u8g2, u8g2_font_inb24_mf);u8g2_DrawStr(u8g2, 51, 30, "g");u8g2_DrawStr(u8g2, 67, 30, "\xb2");u8g2_DrawHLine(u8g2, 2, 35, 47);u8g2_DrawHLine(u8g2, 3, 36, 47);u8g2_DrawVLine(u8g2, 45, 32, 12);u8g2_DrawVLine(u8g2, 46, 33, 12);u8g2_SetFont(u8g2, u8g2_font_4x6_tr);u8g2_DrawStr(u8g2, 1, 54, "github.com/olikraus/u8g2");
}

OLED.c

#include "stm32f4xx.h"
#include "OLED_Font.h"
#include "Delay.h"/*OLED屏地址*/
#define OLED_ADDRESS 0x78//I2C等待超时时间
#define I2C_TIMEOUT 5000/*引脚初始化*/
void OLED_I2C_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);	//使能I2C1时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB时钟GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_I2C1);  //开启PB6的复用功能连接至I2C1GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);/*STM32F407芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */GPIO_InitTypeDef  GPIO_InitStructure;                   //定义结构体配置GPIOGPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;		    //设置GPIO口模式为复用IO模式GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;		//设置GPIO口速度100MhzGPIO_InitStructure.GPIO_OType = GPIO_OType_OD;          //设置GPIO口为开漏输出GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;           //设置GPIO上拉模式GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7; //设置GPIO口GPIO_Init(GPIOB, &GPIO_InitStructure);I2C_DeInit(I2C1);	//将外设I2C1寄存器重设为缺省值I2C_InitTypeDef  I2C_InitStructure;I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;			//工作模式I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//时钟占空比,Tlow/Thigh = 2I2C_InitStructure.I2C_OwnAddress1 = 0x30;	//主机的I2C地址,用不到则随便写,无影响I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	//使能应答位I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//设置地址长度7位I2C_InitStructure.I2C_ClockSpeed = 400000;	//I2C传输速度,400K,根据自己所用芯片手册查看支持的速度。	I2C_Init(I2C1, &I2C_InitStructure);I2C_Cmd(I2C1, ENABLE);
}
// https://blog.zeruns.tech
void HW_I2cWrite(uint8_t *buf,uint8_t len)
{if(len<=0)return ;uint32_t wait_time=0;/* wait for the busy falg to be reset */while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)){wait_time++;if(wait_time>=I2C_TIMEOUT){wait_time=0;break;}}I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))/*EV5,主模式*/{wait_time++;if(wait_time>=I2C_TIMEOUT){wait_time=0;break;}}I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);	//器件地址 -- 默认0x78while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){wait_time++;if(wait_time>=I2C_TIMEOUT){wait_time=0;break;}}for(uint8_t i=0;i<len;i++){I2C_SendData(I2C1, buf[i]);//发送数据while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){wait_time++;if(wait_time>=I2C_TIMEOUT){wait_time=0;break;}}}I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
}

开源项目推荐

  • 画了个 MSP430F149的最小系统板 开源出来了:https://blog.zeruns.tech/archives/713.html
  • STM32F030C8T6最小系统板和流水灯(原理图和PCB):https://blog.zeruns.tech/archives/715.html
  • SY8205同步降压可调DCDC电源模块(原理图和PCB):https://blog.zeruns.tech/archives/717.html
  • 2011年全国电赛题-开关电源模块并联供电系统:https://blog.zeruns.tech/archives/718.html
  • 2007年电赛电源题:30到36V可调升压DCDC模块(UC3843):https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842

推荐阅读

  • 高性价比和便宜的VPS/云服务器推荐: https://blog.vpszj.cn/archives/41.html
  • 怎样搭建个人博客:https://blog.zeruns.tech/archives/218.html
  • 我的世界服务器搭建教程:https://blog.zeruns.tech/tag/mc/
  • STM32读取SHT3x系列温湿度传感器:https://blog.zeruns.tech/archives/700.html
  • 使用VSCode代替Keil实现STM32和51单片机的开发:https://blog.zeruns.tech/archives/690.html
  • 特语云 宁波电信 VPS 性能评测,4核4G 10兆 5G防御 仅需69元/月:https://blog.vpszj.cn/archives/1837.html
  • 雨云 宿迁5900X高防云服务器 性能测评,2核4G 5兆 150G防御 仅需50元/月:https://blog.vpszj.cn/archives/1125.html

http://www.ppmy.cn/news/345502.html

相关文章

华为交换机观察口(observe-port)配置

镜像是指将经过指定端口&#xff08;源端口或者镜像端口&#xff09;的报文复制一份到另一个指定端口&#xff08;目的端口或者观察端口&#xff09;。 镜像可以在不影响设备对报文进行正常处理的情况下&#xff0c;将镜像端口的报文复制一份到观察端口。 端口镜像是指设备复制…

商城购买会员打折满减优惠券商品

文章目录 前言一、代码结构二、UML图三、代码实现3.1.domain3.2.enums3.3.strategy3.4.service3.5.config 四、单元测试五、模式应用六、问题及优化思路6.1.问题6.2.优化 总结 前言 使用策略模式、工厂方法模式、单例模式实现一些购买策略&#xff0c;需求&#xff1a;商城商品…

jenkins显示gitlab的认证报错

描述 使用“List Git branches (and more)”功能&#xff0c;显示gitlab的分支或者标签。报错“org.eclipse.jgit.api.errors.TransportException: http://gitlab.test.com/userCenter/userCenterServer: not authorized” 排查与解决 使用的凭证再gitlab是Maintainer身份&…

Agilent/安捷伦N4010A蓝牙测试仪

产品名称:无线连接测试仪 产品型号: N4010A 厂商品牌:安捷伦 技术指标: N4010A无线连接测试仪可灵活测量最新的无线标准并提高测试效率。 N4010A是一款通用的多格式无线连接测试解决方案&#xff0c;您可以对解决方案进行配置&#xff0c;使其服务于研发、集 成、验证或制造领…

信而泰耦合测试-网络测试仪实操

一、耦合测试原理 1.产生背景​ 常用测试无线设备过程中&#xff0c;将无线设备置于屏蔽箱中&#xff0c;通过无线网卡连接并运行iperf等类似软件的方式检测所述无线设备的吞吐量。 相关技术中将所述无线设备置于所述屏蔽箱中的检测方法&#xff0c;由于所述屏蔽箱存在信号反…

oracle的操作管理sql临时笔记

打印用户下所有的表&#xff1a; 所有信息&#xff1a;select * from all_tables a where a.OWNER upper(用户名); 仅表名&#xff1a;select table_name from all_tables a where a.OWNER upper(用户名);

批处理cmd:for语句的基本用法

一、基础语法 FOR %variable IN (set) DO command [command-parameters] #cmd窗口中 FOR %%variable IN (set) DO command [command-parameters] #批处理文件中之所以要区分cmd窗口和批处理文件两种环境&#xff0c;是因为在这两种环境下&#xff0c;命令语句表现出来的行…

如何避免被 Lombok 撞车,使用时需注意哪些问题?

近年来&#xff0c;Java 开发中使用 Lombok 这个工具已经成为了一种趋势。Lombok 通过简化代码实现了对 Java 开发中常用的重复代码的自动生成&#xff0c;但是却也因为其使用不当而引起了一些问题&#xff0c;例如代码风格不一致、编译错误和运行时异常等。在本文中&#xff0…