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

news2024/12/25 1:13:53

移植好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();      // 初始化OLED
      u8g2_t u8g2;
      u8g2_Init(&u8g2); // 初始化U8g2

      RCC_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口6
      GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;       // 设置GPIO口速度100Mhz
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         // 设置GPIO口为推挽输出
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;       // 设置GPIO上下拉模式
      GPIO_Init(GPIOA, &GPIO_InitStructure);                 // 初始化GPIO

      Delay_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,软件I2C
      u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8g2_gpio_and_delay); // 初始化 u8g2,硬件I2C
      u8g2_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的复用功能连接至I2C1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);
 
	/*STM32F407芯片的硬件I2C: PB6 -- SCL; PB7 -- SDA */
	GPIO_InitTypeDef  GPIO_InitStructure;                   //定义结构体配置GPIO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;		    //设置GPIO口模式为复用IO模式
	GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;		//设置GPIO口速度100Mhz
	GPIO_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 = 2
	I2C_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);//开启I2C1
	while(!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);	//器件地址 -- 默认0x78
	while(!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.coloradmin.cn/o/638378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从分布式到微服务解密“架构”原理与实战笔记

分布式架构与微服务平台是当今IT界的关键技术&#xff0c;也是资深软件工程师和系统架构师必须掌握的核心技术。 微服务、云原生、Kubernetes、Service Mesh是分布式领域的热点技术&#xff0c;它们并不是凭空出现的&#xff0c;一定继承了某些“前辈”的优点。我们不仅要了解…

网络安全零基础之从原理看懂的XSS

01、XSS 的原理和分类 跨站脚本攻击 XSS(Cross Site Scripting)&#xff0c;为了不和层叠样式表(Cascading Style Sheets&#xff0c;CSS)的缩写混淆 故将跨站脚本攻击缩写为 XSS&#xff0c;恶意攻击者往 Web 页面里插入恶意 Script 代码&#xff0c;当用户浏览该页面时&…

软件测试将会赢来陌路?

最近参加了某大厂总监做的一场测试培训&#xff0c;感触颇深&#xff0c;一句话萦绕在耳畔“测试乃至测开&#xff0c;大厂为了降本增效这些都要被优化掉”。去年由他操刀优化了一大批优秀的测试员&#xff01; 实际小酋这几年已经有切身体会&#xff0c;测试野蛮增长的阶段已经…

cv学习--图像分类完结

深度学习面试题05&#xff1a;激活函数sigmod、tanh、ReLU、LeakyRelu、Relu6 - 黎明程序员 - 博客园 (cnblogs.com)

解惑:测试圈网红工具 Jmeter 到底难在哪里

同样的起点&#xff0c;同样的工作时间&#xff0c;为什么别人接那么多项目&#xff0c;你还是在点点点&#xff1b;为什么别人升职了&#xff0c;而你还在原地踏步&#xff1f; 同样的工作内容&#xff0c;为什么别人下班了&#xff0c;你还在加班&#xff1f; 在现在竞争激烈…

Unity入门4——重要组件与API

一、GameObject &#xff08;一&#xff09;成员变量 // 名字 print(this.gameObject.name); this.gameObject.name "Lesson4唐老狮改名"; print(this.gameOb…

手机号码应该如何选择呢?一文解决你的选择困难症!

哈喽大家好&#xff0c;我是搜卡之家葫芦弟。最近有一些小伙伴们私信葫芦弟&#xff0c;想办一个本地的手机号&#xff0c;想着长久一直使用的。既然是长久使用&#xff0c;那就想选一个不错的号码&#xff0c;手机靓号的话&#xff0c;好一点的号码价格又太贵。这不是要在平凡…

参与PK赢大奖!阿里云机器学习平台PAI助力开发者激发AIGC潜能

近年来&#xff0c;随着海量多模态数据在互联网的爆炸性增长和训练深度学习大模型的算力大幅提升&#xff0c;AI生成内容&#xff08;AI Generated Content&#xff0c;AIGC&#xff09;的应用呈现出爆发性增长趋势。为助力开发者能够一站式快速搭建文生图、对话等热门场景应用…

GPT问题摘录go中接口实现和继承

go如何实现接口 package mainimport "fmt"type Shape interface {Area() float64 }type Circle struct {Radius float64 }func (c Circle) Area() float64 {return 3.14 * c.Radius * c.Radius }func main() {c : Circle{Radius: 6.2}fmt.Println(c.Area()) } 首先我…

在线合并Excel文档的HTML网页工具

构建一个在线合并Excel文档的HTML网页工具 导语&#xff1a; 在本篇技术分享文章中&#xff0c;我将向您展示如何使用HTML、CSS和JavaScript构建一个功能强大的在线合并Excel文档的网页工具。这个工具可以帮助用户将多个Excel文件合并成一个文件&#xff0c;提高工作效率和方便…

GDT陶瓷气体放电管会不会烧坏?有没有方向?

首先第一个问题&#xff1a;GDT陶瓷气体放电管会不会烧坏&#xff1f;标准答案&#xff1a;会的。 GDT放电管是一种过压保护开关型元件&#xff0c;通常用在电路防护的前端&#xff0c;起泄放浪涌电流及脉冲过电压防护的效果&#xff0c;在遇到电路设计规划不合理以及选型不当时…

汇川H5U计数器轴编程应用(高速计数和测速应用)

H5U编码器轴和脉冲轴相关应用测试请参看下面文章: H5U PLC本地脉冲轴和本地编码器轴测试_RXXW_Dor的博客-CSDN博客H5U PLC如何通过EtherCAT总线控制伺服运动,请参看下面的博客汇川H5U PLC通过EtherCAT总线控制SV660N和X3E伺服_RXXW_Dor的博客-CSDN博客。https://blog.csdn.n…

IDEA安装教程2023

在本文中&#xff0c;我们将提供关于如何安装 IntelliJ IDEA 的详细步骤。如果您是初学者或只是想尝试一下 IDEA&#xff0c;我们建议您下载 Community 版。如果您需要更多高级功能&#xff0c;可以选择 Ultimate 版。 步骤一&#xff1a;下载 IntelliJ IDEA 首先&#xff0c;…

潮向新世界丨2023亚马逊云科技出海日精彩回顾

2023年6月9日&#xff0c;2023亚马逊云科技“潮向新世界”出海日活动在深圳及线上同步盛大开启。在这场出海盛会中&#xff0c;亚马逊云科技、中国出海企业代表和合作伙伴们分享了哪些精彩内容&#xff1f;他们又对中国企业出海提出了哪些真知灼见&#xff1f; 十年蓄势十年爆…

yolov8中的C2f层

在代码注释中看到C2f其实是 CSP Bottleneck with 2 convolutions 找来CSP的图 大致是把一个output按channel拆成2部分&#xff0c;其中一部分不动&#xff0c; 另一部分过conv&#xff0c;再拼回去&#xff0c; 因为是with 2 convolutions, 会有2个conv. 下面根据代码来走一…

P-One一站式性能测试平台如何安装在国产化操作系统上运行

PerformanceOne&#xff08;简称&#xff1a;P-One&#xff09;是泽众软件自主研发的一套一站式性能测试平台软件产品。 该产品采用 B/S 架构开发&#xff0c;实现了集管理、设计、压测、监控以及分析于一体的全方位性能测试解决方案。可有效提升性能测试技术能力&#xff0c;…

【unity】基础入门 编译错误排查与调试方法

基础入门 编译错误排查与调试方法 一、常见编译错误原因1、环境问题2、代码命名问题二、代码调试方法1、基础调试方法2、高级玩法3、unity调试工具插件一、常见编译错误原因 1、环境问题 1、Win11系统不兼容部分unity版本 考虑换系统吧! 2、可能是系统权限问题,访问不到部分…

经济学人特稿:你应该送孩子去私立学校吗? | 经济学人20230610版社论双语精翻

“升学季”特稿&#xff1a;2023年6月10日《经济学人》周报封面文章《送孩子去私立学校——值吗?》&#xff08;Are private schools worth it?&#xff09; International | Studying for success 为了成功而学习 Should you send your children to private school? 你应该…

跨境电商对于撸卡、撸货、采退都需要哪些技术要求

在当前跨境电商环境下&#xff0c;从事撸卡、撸货或者采退等业务的人员日益增多。然而&#xff0c;由于大部分人使用的环境系统相对简单&#xff0c;例如云手机、一键新机工具IP、指纹浏览器IP、虚拟机等&#xff0c;缺乏足够复杂的防护机制&#xff0c;因此&#xff0c;经常会…

马拉松资讯获取及报名渠道分享

1、马拉松赛事 1.1 马拉松赛事组别 马拉松赛事分为全程马拉松&#xff08;42.195公里&#xff09;、半程马拉松&#xff08;21.0975公里&#xff09;、短程跑&#xff08;健康跑、亲子跑、家庭跑之类的&#xff09;这几个类别&#xff0c;大型田协认证赛事三种类型都有&#…