目录

  • 前言
  • 一、建立工程
  • 二、编写和移植
    • 前期准备
    • 驱动部分修改
  • 三、使用和验证
  • 结论

(2022年1月22日重制)本文主要是移植带简单图形库的程序,如果只是实现DMA控制,建议看【0.96寸 OLED屏实现1500Fps的帧率】STM32 软件、硬件SPI、I2C驱动总结

本文参考例程
提取码: sch3

前言

本文将介绍在STM32 HAL库下实现硬件IIC+DMA控制0.96寸 OLED屏,以及OLED图形库的移植。


一、建立工程

平台:野火指南者STM32F103VET6 + STM32Cube MX + Keil MDK 5

(示例)
1.选择所用MCU。

2.选择启用晶振
3.选择Debug方式
4.设置时钟树

5.选择合适的引脚开启I2C
6.加入I2Cx_TX的DMA
7.开启12Cx_event_interrupt
8.配置完后建立工程

二、编写和移植

本节主要介绍移植标准库编写的oledlib图形库过程。
oledlib库原作者:MjGame
原作例程开源地址:MjGame 的例程
本文使用的oledlib库修订者:一只程序缘
本文所要移植库的开源地址:一只程序缘 的例程

git clone https://gitee.com/jiezhuonew/oledlib.git

(示例):

前期准备

1.先导入所需的文件
所用Delay文件参考《无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)》



2.将delay.h改为我们所用的Delay.h文件


修改u16为unsigned short或uint16_t

驱动部分修改

  1. 在oled_config.h中添加#include “main.h”,并修改TRANSFER_METHOD

  2. 在oled_driver.h和oled_driver.c中注释掉标准库相关的东西:

    此时编译不再弹出错误。

  3. 在oled_driver.c修改I2C_WriteByte函数

 /**@brief  I2C_WriteByte,向OLED寄存器地址写一个byte的数据@param  addr:寄存器地址data:要写入的数据@retval 无*/void I2C_WriteByte(uint8_t addr, uint8_t data){//      while (I2C_GetFlagStatus(I2CX, I2C_FLAG_BUSY));//       I2C_GenerateSTART(I2CX, ENABLE);//开启I2C1
//      while (!I2C_CheckEvent(I2CX, I2C_EVENT_MASTER_MODE_SELECT)); /*EV5,主模式*///      I2C_Send7bitAddress(I2CX, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
//      while (!I2C_CheckEvent(I2CX, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//        I2C_SendData(I2CX, addr);//寄存器地址
//      while (!I2C_CheckEvent(I2CX, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//     I2C_SendData(I2CX, data);//发送数据
//      while (!I2C_CheckEvent(I2CX, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//     I2C_GenerateSTOP(I2CX, ENABLE);//关闭I2C1总线extern I2C_HandleTypeDef hi2c1;HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS, addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);}
  1. 在oled_driver.c修改OLED_CLS函数
void OLED_CLS(void)//清屏
{//  unsigned char m, n;
//  for (m = 0; m < 8; m++)
//  {//      WriteCmd(0xb0 + m); //page0-page1
//      WriteCmd(0x00);   //low column start address
//      WriteCmd(0x10);   //high column start address
//      for (n = 0; n < 128; n++)
//      {//          WriteDat(0x00);
//      }
//  }extern unsigned char ScreenBuffer[SCREEN_PAGE_NUM][SCREEN_COLUMN];uint16_t i;for (i = 0; i < SCREEN_PAGE_NUM * SCREEN_COLUMN; ++i){ScreenBuffer[0][i] = 0;}OLED_FILL(ScreenBuffer[0]);
}
  1. 在oled_driver.c修改OLED_FILL函数
void OLED_FILL(unsigned char BMP[])
{//  uint8_t i, j;unsigned char *p;p = BMP;//   for (i = 0; i < 8; i++)
//  {//      WriteCmd(0xb0 + i); //page0-page1
//      WriteCmd(0x00);   //low column start address
//      WriteCmd(0x10);//       for (j = 0; j < 128; j++)
//      {//          WriteDat(*p++);
//      }
//  }extern I2C_HandleTypeDef hi2c1;while(hi2c1.State != HAL_I2C_STATE_READY);HAL_I2C_Mem_Write_DMA(&hi2c1, OLED_ADDRESS, 0x40, I2C_MEMADD_SIZE_8BIT, p, SCREEN_PAGE_NUM * SCREEN_PAGEDATA_NUM);
}
  1. 注意到
//请将此函数放入1ms中断里,为图形提供时基
//系统时间基准主要用于FrameRateUpdateScreen()中固定帧率刷新屏幕
void OledTimeMsFunc(void)
{if(OledTimeMs != 0x00){ OledTimeMs--;}
}

故到stm32f1xx_it.c中声明并添加此函数
若未使用FreeRTOS则在滴答定时器中断中添加,反之在对应定时器中断中添加:

/*** @brief This function handles System tick timer.*/
void SysTick_Handler(void)
{/* USER CODE BEGIN SysTick_IRQn 0 *//* USER CODE END SysTick_IRQn 0 */HAL_IncTick();/* USER CODE BEGIN SysTick_IRQn 1 */OledTimeMsFunc();/* USER CODE END SysTick_IRQn 1 */
}
  1. 修改OLED_Init函数
void OLED_Init(void)
{WriteCmd(0xAE); //display offWriteCmd(0x20); //Set Memory Addressing Mode
//  WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,InvalidWriteCmd(0x00); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,InvalidWriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7WriteCmd(0xc8); //Set COM Output Scan DirectionWriteCmd(0x00); //---set low column addressWriteCmd(0x10); //---set high column addressWriteCmd(0x40); //--set start line addressWriteCmd(0x81); //--set contrast control registerWriteCmd(0xff); //亮度调节 0x00~0xffWriteCmd(0xa1); //--set segment re-map 0 to 127WriteCmd(0xa6); //--set normal displayWriteCmd(0xa8); //--set multiplex ratio(1 to 64)WriteCmd(0x3F); //WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentWriteCmd(0xd3); //-set display offsetWriteCmd(0x00); //-not offsetWriteCmd(0xd5); //--set display clock divide ratio/oscillator frequencyWriteCmd(0xf0); //--set divide ratioWriteCmd(0xd9); //--set pre-charge periodWriteCmd(0x22); //WriteCmd(0xda); //--set com pins hardware configurationWriteCmd(0x12);WriteCmd(0xdb); //--set vcomhWriteCmd(0x20); //0x20,0.77xVccWriteCmd(0x8d); //--set DC-DC enableWriteCmd(0x14); //WriteCmd(0xaf); //--turn on oled panelOLED_CLS();
}

其中 WriteCmd(0x20); WriteCmd(0x00); 两句函数将SSD1306的寻址方式修改为了水平寻址,能够一次性对OLED内部Graphic Display Data RAM (GDDRAM)整体更新。
见firestaradmin大佬的
《STM32 DMA-IIC刷新OLED屏(理论可达42+帧率)》
和Coder_BCM大佬的
《基于STM32F407的 中景园0.96寸OLED(IIC)的程序升级(DMA+IIC + 显存Buffer)》)
若发现行错位,则修改

 WriteCmd(0xd3); //-set display offsetWriteCmd(0x00 加或减 8的倍数);

三、使用和验证

添加test.c,该文件源自原例程,有修改。
需要注意的是调用OLED_ShowCHinese函数的文件和oled_font.c文件的编码格式需一致:GBK。当一个为UTF-8,一个为GBK时会显示不出汉字。

/*Copyright (c) [2019] [一只程序缘 jiezhuo][https://gitee.com/jiezhuonew/oledlib] is licensed under the Mulan PSL v1.You can use this software according to the terms and conditions of the Mulan PSL v1.You may obtain a copy of Mulan PSL v1 at:http://license.coscl.org.cn/MulanPSLTHIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULARPURPOSE.See the Mulan PSL v1 for more details.==========此函数是oledlib图形库的综合测试函数 基本上使用到了里面的所有内容==================目前接线说明:仅仅使用到0.96寸7引脚oled 使用SPI通信方式(具体见oled_drive.c)D0  = SCK  -> PA4     (屏幕批次不同丝印可能不同)D1  = SDA  -> PA5RES = RES  ->    PA6DC  = DC   ->    PA7CS  = SCS  ->    PA8软件模块:oled    spi驱动beep   蜂鸣器 提示或作为led表示运行状态timer3    中断1ms 可为图形库提供时间基准uart   可作为串口连接电脑打印调试xinxidelay 系统自带的时间设置与提供延时函数视频演示信息:单片机 --- STM32F103oled   --- 采用4线SPI的7引脚0.96寸黑白双色oled图形库功能:在oledlib文件夹下作出多个c文件的分类 每个c文件的开头都有对该文件的简要说明包括打点 画线 矩形 三角形 圆 椭圆 圆角矩形 的边缘与填充还包括 字符显示 汉字显示 位图显示最后还有 oled模拟串口printf的debug字符显示:针对于字符有4种大小0-1-2-3 具体参见SetFontSize()基于大家的使用习惯0是一般大家习惯用的            此时y只能为0-7对应显示的7大行       注意:此时x为地基大行 y为第几列2 3都是基于1的等比例放大 此时y能为0-6x对应显示的每一小行      此时为正常坐标系 x为第几小行 y为第几列中文汉字显示:中文汉字的大小为16x16       此时y也只能为0-7对应显示的7大行      注意:此时x为地基大行 y为第几列取模说明:取模软件采用PCtoLCD2002字符 汉字 和 位图的取模方式和设置都在font.c和bmp.c中有注释用户调用函数:在draw_api.h中提供了所有函数的接口 可在其中查看所有函数的简要功能移植说明:1.移植图形库仅需要准备好底层驱动 在oled_driver.c中作出修改即可 在该c文件开头有说明2.若需要对test.c中的动画效果进行测试 还需要为函数OledTimeMsFunc()提供时间基准3.库函数使用的延时函数是DelayMs() 移植后由单片机型号补充调整DelayMs()图形库本质:该图形库本质为对一数组进行操作 然后刷新整个数组到屏幕中实现效果因此相似点阵屏(oled/TFT/大小尺寸/点阵/液晶)也可以使用 根据像素可参见oled_config.h详细的注释:如果是使用为用途 了解test.c和draw_api.c即可 做出花里胡哨的UI界面在本人阅读代码和注释的过程中 发现很多结构和函数都值得学习 仔细理解理解GUI和本图形库的关系:gui是一个以图形界面来进行人机交互的系统 gui是拥有窗口、菜单、控件、对话框等交互的图形接口这个只是一个图形库 类似于c++上的easyx图形库 是没有图形接口的 只提供绘图和显示的功能图形库可以算是gui的一部分,也可以自己以图形库继续封装为gui库代码容量:除去位图 Code=26254 RO-data=3754 RW-data=100 ZI-data=4108原作的话:为了做这个绘制图形库,我参考了很多其他的算法,花了大量时间去做移植优化,其中参考最多的是github中的arduboy游戏机,大部分图形是移植它的,现在是最初的初稿版本,还有很多地方需要优化改进。我想要将这个图形库做大,后续会加入更多有趣的东西,和模块,代码都是用最简单的方式编写的,都是开源的。后续也会加上注释说明,让更多人以单片机和oled屏来入门硬件编程,如果你使用了该库,请标明出处。b站关注oximeterg,可获取该库的最新版本和消息。注意:绘制填充或实心的图形,影响刷新效率(待优化中)
*/// #include "test.h"
#include "draw_api.h"
#include "../Delay/Delay.h"
#include "stdlib.h"
#include "stdio.h"
#include "math.h"void ShowStars(void);
void ShowWatch(void);
void ShowPolygon(void);
void ShowSnow(void);void demo(void)
{int i,j;//demo演示ClearScreen();DrawBitmap(0,0,Panda,128,64);UpdateScreen();DelayMs(1000);//字符ClearScreen();SetFontSize(1);DrawString(0,0,"test");UpdateScreen();DelayMs(1000);SetFontSize(2);DrawString(0,8,"test");UpdateScreen();DelayMs(1000);SetFontSize(3);DrawString(0,24,"test");UpdateScreen();DelayMs(1000);//汉字测试ClearScreen();OLED_ShowCHinese(0,0,"一只程序缘");UpdateScreen();DelayMs(1000);OLED_ShowCHinese(2,0,"一只程序缘一只程序缘一只程序缘一只程序缘");UpdateScreen();DelayMs(1000);//数字测试ClearScreen();for(i=0; i<300; i++){SetFontSize(0);DrawNum(0,96,i,4);SetFontSize(1);DrawNum(0,16,i,4);SetFontSize(2);DrawNum(0,24,i,4);SetFontSize(3);DrawNum(0,40,i,4);UpdateScreen();}//串口debugDelayMs(100);ClearScreen();for(i=0; i<256; i++){OledPrintf("num:%d cha:%c hex:%x\r\n",i,i,i);}DelayMs(100);//划线ClearScreen();for(i=0; i<20; i++){DrawLine(0,0,i*10,63);UpdateScreen();}for(i=0; i<20; i++){DrawLine(128,0,128-i*10,63);UpdateScreen();}DelayMs(100);//矩形for(j=0; j<2; j++){if(j==0)ClearScreen();for(i=0; i<31; i+=2){DrawRect2(i*2,i,128-i*4,64-2*i);//画矩形外框UpdateScreen();}if(j==0)ClearScreen();DelayMs(100);for(i=31; i>0; i-=2){DrawFillRect2(i*2,i,128-i*4,64-2*i);//画实心矩形UpdateScreen();}SetDrawColor(pix_black);//划线颜色SetFillcolor(pix_black);//填充颜色}SetDrawColor(pix_white);SetFillcolor(pix_white);//圆角矩形for(j=0; j<2; j++){if(j==0)ClearScreen();for(i=0; i<25; i+=2){DrawRoundRect(i*2,i,128-i*4,64-2*i,8);UpdateScreen();}if(j==0)ClearScreen();DelayMs(100);for(i=25; i>2; i-=2){DrawfillRoundRect(i*2,i,128-i*4,64-2*i,8);UpdateScreen();}SetDrawColor(pix_black);SetFillcolor(pix_black);}SetDrawColor(pix_white);SetFillcolor(pix_white);DelayMs(100);//椭圆ClearScreen();DrawEllipse(63,31,63,31);UpdateScreen();DelayMs(500);ClearScreen();DrawEllipse(63,31,16,31);UpdateScreen();DelayMs(500);ClearScreen();DrawFillEllipse(63,31,63,31);UpdateScreen();DelayMs(500);ClearScreen();DrawFillEllipse(63,31,16,31);UpdateScreen();DelayMs(500);//圆ClearScreen();DrawCircle(63,31,30);UpdateScreen();DelayMs(500);ClearScreen();DrawFillCircle(63,31,30);UpdateScreen();DelayMs(500);//三角形ClearScreen();DrawTriangle(5,10,100,30,60,50);UpdateScreen();DelayMs(500);ClearScreen();DrawFillTriangle(5,10,100,30,60,50);UpdateScreen();DelayMs(500);//绘制图形图片 位图ClearScreen();DrawBitmap(0,0,BmpTest1,16,16);UpdateScreen();DelayMs(500);DrawBitmap(16,0,BmpTest2,32,32);UpdateScreen();DelayMs(500);DrawBitmap(48,0,BmpTest3,64,64);UpdateScreen();DelayMs(500);//圆弧ClearScreen();for(i=0; i<360; i++){DrawArc(63,31,30,0,i);  //画圆弧UpdateScreen();DelayMs(25);ClearScreen();}DrawCircle(63,31,30);        //画圆UpdateScreen();DelayMs(100);for(i=0; i<10; i++){DrawFillCircle(63,31,i);  //在中心填充圆DrawCircle(63,31,30);UpdateScreen();DelayMs(25);ClearScreen();}//绕点for(i=0; i<720; i++) //转720度2圈{TypeXY temp;SetAngle(i);                    //设置角度SetRotateCenter(63,31);           //设置圆心temp=GetRotateXY(63,31+30);     //讲已知坐标旋转角度DrawFillCircle(temp.x,temp.y,5);//画出算出的位置DrawCircle(63,31,30);           //画出轨迹DrawFillCircle(63,31,10);     //填充中心UpdateScreen();                   //更新屏幕ClearScreen();}//向右边平移for(i=0; i<95; i++){TypeXY temp;                  //其实就是上面继续旋转SetAngle(720+i);               //画图的时候x+偏移量SetRotateCenter(63+i,31);temp=GetRotateXY(63+i,31+30);DrawFillCircle(temp.x,temp.y,5);DrawCircle(63+i,31,30);DrawFillCircle(63+i,31,10);UpdateScreen();ClearScreen();}//动画ShowStars();   //星空动画ShowWatch();  //时钟动画ShowPolygon();    //多边形动画
//  ShowSnow();     //下雪动画DelayMs(100);//结束ClearScreen();SetFontSize(2);DrawString(8,16," Show End ");    //显示字符串UpdateScreen();DelayMs(1000);ClearScreen();DrawBitmap(0,10,Like,128,40); //三连图片UpdateScreen();DelayMs(1000);
}/
//星空动画
void ShowStars(void)
{int i;int count=0;int fps=60;typedef struct START{short x;short y;short speed;unsigned char speedcount;unsigned char isexist;} Star;Star star[128]= {0};srand(2);for(i=0; i<128; i++){if(star[i].isexist==0){//设置128个()星星的初始信息star[i].x=rand()%127;        //随机生成初始x坐标star[i].y=rand()%64;        //随机生成y的坐标star[i].speedcount=0;star[i].speed=rand()%8+1; //1-8的数star[i].isexist=1;}}while(1){if(FrameRateUpdateScreen(fps)==1)    //在此函数中定时刷新屏{count++;if(count>=fps*10)            //10秒钟时间到达之后跳出循环结束动画return;}//此段函数一直在运行//依次画出128个星星for(i=0; i<128; i++){//如果这一个星星已经移动到退出屏幕界面//则在最左侧重新生成一颗新星星if(star[i].isexist==0){star[i].x=0;star[i].y=rand()%64;star[i].speed=rand()%6+1;star[i].speedcount=0;star[i].isexist=1;}else{star[i].speedcount++;if(star[i].x>=124)            //标记已经退出屏幕star[i].isexist=0;//清除上一个时刻画的星星(的尾巴) 不管有没有操作 都进行清除操作SetDrawColor(pix_black);DrawLine(star[i].x,star[i].y,star[i].x,star[i].y);SetDrawColor(pix_white);if(star[i].speedcount==star[i].speed)    //运行时间到了一定的长度{star[i].speedcount=0;                //复位运行时间并向右移一格star[i].x+=1;                       //总之星星的结束需要在这经历124次}                                        //只不过有的更快 就能移动更快//从头到尾画出整条星星 不管星星是否已经变化DrawLine(star[i].x, star[i].y, star[i].x+(6/star[i].speed)-1, star[i].y);}}}
}
//时钟动画
//钟 小时 分钟 秒,
void RoundClock(int hours,int minute,int sec)
{unsigned char i=0;TypeXY hourspoint,minutepoint,secpoint,tmp1,tmp2;//时针SetRotateValue(63,31,hours*30+(minute*30)/60,1);hourspoint=GetRotateXY(63-14,31);DrawLine(63,31,hourspoint.x,hourspoint.y);//分针SetRotateValue(63,31,minute*6+(sec*6)/60,1);minutepoint=GetRotateXY(63-21,31);DrawLine(63,31,minutepoint.x,minutepoint.y);//秒针SetRotateValue(63,31,sec*6,1);secpoint=GetRotateXY(63-28,31);DrawLine(63,31,secpoint.x,secpoint.y);//表盘for(i=0; i<12; i++){SetRotateValue(63,31,i*30,1);tmp1=GetRotateXY(63-29,31);tmp2=GetRotateXY(63-24,31);DrawLine(tmp1.x,tmp1.y,tmp2.x,tmp2.y);}DrawFillCircle(63,31,2);DrawCircle(63,31,30);UpdateScreen();DelayMs(25);ClearScreen();
}void ShowWatch(void)
{int i,j,z;int count=0;for(i=0; i<12; i++)for(j=0; j<60; j++)for(z=0; z<60; z++){RoundClock(i,j,z);count++;if(count>=800)return;}
}/
//多边形动画
void ShowPolygon(void)
{int x0=63,y0=31;     //正多边形的外接圆的圆心unsigned char i =0,j;int n=1,r=31;          //画正n边形 大小半径31int v=1,count=0;        //每个相邻的多边形隔1 画count次后退出int x[30],y[30];while(1){ClearScreen();for(i=0; i<n; i++){x[i]=r*cos(2*3.1415926*i/n)+x0;y[i]=r*sin(2*3.1415926*i/n)+y0;}for(i=0; i<=n-2; i++){for(j=i+1; j<=n-1; j++)DrawLine(x[i],y[i],x[j],y[j]);}n+=v;if(n==20||n==0)v=-v;UpdateScreen();DelayMs(25);        //延时停顿100msif(++count==90){count=0;return ;}}
}//下雪的函数 和星空类似
void ShowSnow(void)
{int a[66],i,num=0;int count=0;int fps=60;struct Snow{short x;short y;short speed;} snow[100];srand(1);for(i=0; i<66; i++)a[i]=(i-2)*10;ClearScreen();while(1){if(FrameRateUpdateScreen(fps)==1)    //在此函数中定时刷新屏{count++;if(count>=fps*10)            //10秒钟时间到达之后跳出循环结束动画return;}if(num!=100){snow[num].speed=1+rand()%4;i=rand()%66;snow[num].x=a[i];snow[num].y=0;num++;}for(i=0; i<num; i++){snow[i].y+=snow[i].speed;DrawPixel(snow[i].x,snow[i].y+1);DrawPixel(snow[i].x+1,snow[i].y);DrawPixel(snow[i].x,snow[i].y);DrawPixel(snow[i].x-1,snow[i].y);DrawPixel(snow[i].x,snow[i].y-1);if(snow[i].y>63){snow[i].y=0;}}UpdateScreen();DelayMs(25);ClearScreen();}
}

在main.c文件中包含必要的头文件

//...
#include "main.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../Delay/Delay.h"
#include "../oledlib/draw_api.h"
/* USER CODE END Includes */
//...

添加初始化函数并测试demo

//.../* USER CODE BEGIN 2 */delay_init();InitGraph();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */void demo(void);demo();}/* USER CODE END 3 */
//...

效果如下



结论

相比于普通的硬件IIC控制屏幕,改用DMA驱动后,刷新率得到了很大提升。其速度提升的原理为《STM32 DMA-IIC刷新OLED屏(理论可达42+帧率)》

STM32 HAL 硬件IIC+DMA+简单图形库控制OLED相关推荐

  1. 【STM32】 硬件IIC 驱动SSD1302(0.96 OLED模块) -- 3/4 OLED的命令表 学习

    书接上回 文章1:[STM32] 硬件IIC 驱动SSD1306(0.96 OLED模块) – 1/4 库函数 学习 文章2:[STM32] 硬件IIC 驱动SSD1302(0.96 OLED模块) ...

  2. 【STM32】CubeMX+HAL库之 硬件IIC+DMA控制OLED(兼容SSD1306SH1106驱动)

    [STM32]CubeMX+HAL库之 硬件IIC+DMA控制1.3寸OLED 前言 目前网上大多数驱动OLED屏都采用软件IIC,因为HAL库的升级使得硬件IIC的稳定性得到了保障,所以想采用硬件I ...

  3. 使用STM32+硬件IIC+DMA驱动GT系列触摸屏(GT911)

    使用STM32+硬件IIC+DMA驱动GT系列触摸屏(GT911) 初始化代码 /** @brief GT911 初始化程序* @param None* @retval None*/ void GT9 ...

  4. 【国货之光】GD32E230F4使用硬件IIC+DMA读写24C04

    在很久很久以前,我就写过GD32E230替换STM32F031的帖子,主要介绍了USART和SPI的外设移植开发,当时IIC使用的是软件i2c,没有介绍的价值.在使用IIC时,大多数我们都是采用软件的 ...

  5. STM32 HAL库IIC驱动

    1. STM32上IIC的一些争议 关于STM32的IIC驱动,网上有很多争论,究竟是使用STM32自带的硬件IIC还是用IO口和软件模拟IIC呢?下面这个图形象展示了这些争论.总结一些:ST为了规避 ...

  6. 【STM32-I2C学习总结】STM32:硬件-IIC详解 , 固件库编程 , 手把手教你实现IIC

    STM32:硬件-IIC详解 , 固件库编程 , 手把手教你实现IIC 一 .I2C物理层 二.协议层 1.I2C基本读写过程 (1)主机写数据到从机 (2)主机由从机中读数据 (3)I2C 通讯复合 ...

  7. STM32 HAL库 串口DMA接收不定长数据

    STM32 HAL库 串口DMA接收不定长数据 整体思路:我是用的CUBEMX软件生成的工程,使能了两个串口,串口2用来接收不定长的数据,串口1用来发送串口2接收到的数据:串口2我找了一个UBLOX卫 ...

  8. STM32 HAL 驱动I2C总线0.91寸OLED模块(基于SSD1306显示驱动芯片)

    STM32 HAL 驱动I2C总线0.91寸OLED模块(基于SSD1306显示驱动芯片) 基于SSD1306驱动芯片的OLED模块有多种型号,有0.91英寸,0.96英寸等等.OLED采用单色显示方 ...

  9. STM32 HAL库 串口DMA(收发)和STM32串口中断接收(接收时间管理机制)+ESP8266 wifi模组通信问题

    一.HAL库 串口 DMA+ESP8266模组通信问题 用STM32 HAL库串口的DMA发送和空闲中断接收处理数据,单片机发送AT指令给ESP8266 wifi模组问题:单片机连续几次给wifi模组 ...

最新文章

  1. EDIUS输出到文件
  2. SpringCloud系列七:Hystrix 熔断机制(Hystrix基本配置、服务降级、HystrixDashboard服务监控、Turbine聚合监控)...
  3. 全球可持续发展标准化城市联盟首个海外会议基地落户杭州
  4. oracle omf管理 and asm omf
  5. 常指针、指向常量的指针、指向常量的常指针
  6. 用keras作CNN卷积网络书本分类(书本、非书本)
  7. 实现函数克隆_哪个更好的选择:克隆或复制构造函数?
  8. 【实际经验】一台无系统PC机连接局域网产生的问题
  9. pythonplot画多图间隔,matplotlib实现一页多图
  10. 苹果手机录屏软件_手机端录屏软件哪个好 手机上最好的录屏软件
  11. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day8】 —— Redis2
  12. 【ViT论文】Not All Patches are What You Need: Expediting Vision Transformers via Token Reorganizations
  13. BGP路由协议的那些事?(上)
  14. Python正则表达式【转】
  15. 带有三角函数的计算机,三角函数计算器
  16. shell 学习笔记---运算符
  17. 下列哪项不是python中对文件的读取操作-Python—文件读写操作
  18. Spring rebooted --重新认识Spring
  19. 2-5.基金的信息披露
  20. LOJ #2878. 「JOISC 2014 Day2」邮戳拉力赛 动态规划+括号序列

热门文章

  1. 【Go语言】面向对象扩展——接口
  2. FTP文件共传输服务
  3. [WCF安全系列]绑定、安全模式与客户端凭证类型:NetNamedPipeBinding、NetTcpBinding与NetMsmqBinding...
  4. 两块网卡实现多台机器共享上网
  5. Linux下Socket网络编程
  6. centos配置ftp
  7. 资深工程师为何否定这种单例模式
  8. 熊猫数据集_对熊猫数据框使用逻辑比较
  9. 相似图像搜索的哈希算法思想及实现(差值哈希算法和均值哈希算法)
  10. 1713. 得到子序列的最少操作次数