学习目标

  1. 掌握移植方法
  2. 掌握解决移植失败的思路
  3. 掌握调试方式

    学习内容

    需求

    214.png216.png

官方测试示例

选择对应的平台

测试示例中,找到芯片对应平台,我们选择的是STM32F407
217.png

修改例程

已知错误修改:

  1. #ifndef u8
  2. #define u8 uint8_t
  3. #endif
  4. #ifndef u16
  5. #define u16 uint16_t
  6. #endif
  7. #ifndef u32
  8. #define u32 uint32_t
  9. #endif

引脚定义修改:

  1. #define SCL_PORT_RCC RCC_AHB1Periph_GPIOB
  2. #define SCL_PORT GPIOB
  3. #define SCL_PIN GPIO_Pin_6
  4. #define SDA_PORT_RCC RCC_AHB1Periph_GPIOB
  5. #define SDA_PORT GPIOB
  6. #define SDA_PIN GPIO_Pin_7
  7. //-----------------OLED端口定义----------------
  8. #define OLED_SCL_Clr() GPIO_ResetBits(SCL_PORT,SCL_PIN)//SCL
  9. #define OLED_SCL_Set() GPIO_SetBits(SCL_PORT,SCL_PIN)
  10. #define OLED_SDA_Clr() GPIO_ResetBits(SDA_PORT,SDA_PIN)//DIN
  11. #define OLED_SDA_Set() GPIO_SetBits(SDA_PORT,SDA_PIN)
  12. //#define OLED_RES_Clr() GPIO_ResetBits(GPIOD,GPIO_Pin_4)//RES
  13. //#define OLED_RES_Set() GPIO_SetBits(GPIOD,GPIO_Pin_4)

初始化逻辑修改:

  1. GPIO_InitTypeDef GPIO_InitStructure;
  2. ////////////////// SCL
  3. RCC_AHB1PeriphClockCmd(SCL_PORT_RCC, ENABLE);
  4. //GPIO初始化设置
  5. GPIO_InitStructure.GPIO_Pin = SCL_PIN;
  6. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  7. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  8. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  9. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  10. GPIO_Init(SCL_PORT, &GPIO_InitStructure);//初始化
  11. ////////////////// SDA
  12. RCC_AHB1PeriphClockCmd(SDA_PORT_RCC, ENABLE);
  13. //GPIO初始化设置
  14. GPIO_InitStructure.GPIO_Pin = SDA_PIN;
  15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  16. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  17. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  18. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  19. GPIO_Init(SDA_PORT, &GPIO_InitStructure);//初始化
  20. // OLED_RES_Clr();
  21. // delay_ms(200);
  22. // OLED_RES_Set();

烧录

修改芯片设置,修改烧录方式为DAP Link,烧录到当前GD32F470芯片中

测试结果

失败。无法工作。
通常碰到这种情况,我们需要进行分析。

  1. 环境问题
  2. 协议问题

    STM32测试环境

    采用自己的模板环境。

    测试代码构建

    ```c u8 t=’ ‘; OLED_Init();

while(1) { OLED_ShowPicture(0,0,128,64,BMP1,1); OLED_Refresh(); delay_1ms(500); OLED_Clear(); OLED_ShowChinese(0,0,0,16,1);//中 OLED_ShowChinese(18,0,1,16,1);//景 OLED_ShowChinese(36,0,2,16,1);//园 OLED_ShowChinese(54,0,3,16,1);//电 OLED_ShowChinese(72,0,4,16,1);//子 OLED_ShowChinese(90,0,5,16,1);//技 OLED_ShowChinese(108,0,6,16,1);//术 OLED_ShowString(8,16,”ZHONGJINGYUAN”,16,1); OLED_ShowString(20,32,”2014/05/01”,16,1); OLED_ShowString(0,48,”ASCII:”,16,1); OLED_ShowString(63,48,”CODE:”,16,1); OLED_ShowChar(48,48,t,16,1);//显示ASCII字符 t++; if(t>’~’)t=’ ‘; OLED_ShowNum(103,48,t,3,16,1); OLED_Refresh(); delay_1ms(500); OLED_Clear(); OLED_ShowChinese(0,0,0,16,1); //1616 中 OLED_ShowChinese(16,0,0,24,1); //2424 中 OLED_ShowChinese(24,20,0,32,1);//3232 中 OLED_ShowChinese(64,0,0,64,1); //6464 中 OLED_Refresh(); delay_1ms(500); OLED_Clear(); OLED_ShowString(0,0,”ABC”,8,1);//68 “ABC” OLED_ShowString(0,8,”ABC”,12,1);//612 “ABC” OLED_ShowString(0,20,”ABC”,16,1);//816 “ABC” OLED_ShowString(0,36,”ABC”,24,1);//1224 “ABC” OLED_Refresh(); delay_1ms(500); OLED_ScrollDisplay(11,4,1); }

  1. 修改一些API命名,比如delayapi
  2. <a name="GxKfY"></a>
  3. #### 驱动移植
  4. 可以将之前出错的API移植过来。
  5. ```c
  6. #ifndef __OLED_H
  7. #define __OLED_H
  8. #include "stm32f4xx.h"
  9. #include "stdlib.h"
  10. #ifndef u8
  11. #define u8 uint8_t
  12. #endif
  13. #ifndef u16
  14. #define u16 uint16_t
  15. #endif
  16. #ifndef u32
  17. #define u32 uint32_t
  18. #endif
  19. #define SCL_PORT_RCC RCC_AHB1Periph_GPIOB
  20. #define SCL_PORT GPIOB
  21. #define SCL_PIN GPIO_Pin_6
  22. #define SDA_PORT_RCC RCC_AHB1Periph_GPIOB
  23. #define SDA_PORT GPIOB
  24. #define SDA_PIN GPIO_Pin_7
  25. #define SDA_STATE() GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)
  26. #define SDA_IN() { GPIO_InitTypeDef GPIO_InitStructure; \
  27. GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
  28. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; \
  29. GPIO_Init(SDA_PORT, &GPIO_InitStructure); }
  30. #define SDA_OUT() { GPIO_InitTypeDef GPIO_InitStructure; \
  31. GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
  32. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; \
  33. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; \
  34. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; \
  35. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; \
  36. GPIO_Init(SDA_PORT, &GPIO_InitStructure); }
  37. //#define SDA_IN() 0
  38. //#define SDA_OUT() 0
  39. //-----------------OLED端口定义----------------
  40. #define OLED_SCL_Clr() GPIO_ResetBits(SCL_PORT,SCL_PIN)//SCL
  41. #define OLED_SCL_Set() GPIO_SetBits(SCL_PORT,SCL_PIN)
  42. #define OLED_SDA_Clr() GPIO_ResetBits(SDA_PORT,SDA_PIN)//DIN
  43. #define OLED_SDA_Set() GPIO_SetBits(SDA_PORT,SDA_PIN)
  44. //#define OLED_RES_Clr() GPIO_ResetBits(GPIOD,GPIO_Pin_4)//RES
  45. //#define OLED_RES_Set() GPIO_SetBits(GPIOD,GPIO_Pin_4)
  46. #define OLED_CMD 0 //写命令
  47. #define OLED_DATA 1 //写数据
  48. void OLED_ClearPoint(u8 x,u8 y);
  49. void OLED_ColorTurn(u8 i);
  50. void OLED_DisplayTurn(u8 i);
  51. void I2C_Start(void);
  52. void I2C_Stop(void);
  53. void I2C_WaitAck(void);
  54. void Send_Byte(u8 dat);
  55. void OLED_WR_Byte(u8 dat,u8 mode);
  56. void OLED_DisPlay_On(void);
  57. void OLED_DisPlay_Off(void);
  58. void OLED_Refresh(void);
  59. void OLED_Clear(void);
  60. void OLED_DrawPoint(u8 x,u8 y,u8 t);
  61. void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
  62. void OLED_DrawCircle(u8 x,u8 y,u8 r);
  63. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
  64. void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
  65. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
  66. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
  67. void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
  68. void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
  69. void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
  70. void OLED_Init(void);
  71. #endif
  1. #include "oled.h"
  2. #include "stdlib.h"
  3. #include "oledfont.h"
  4. #include "systick.h"
  5. u8 OLED_GRAM[144][8];
  6. //反显函数
  7. void OLED_ColorTurn(u8 i)
  8. {
  9. if(i==0)
  10. {
  11. OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
  12. }
  13. if(i==1)
  14. {
  15. OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
  16. }
  17. }
  18. //屏幕旋转180度
  19. void OLED_DisplayTurn(u8 i)
  20. {
  21. if(i==0)
  22. {
  23. OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
  24. OLED_WR_Byte(0xA1,OLED_CMD);
  25. }
  26. if(i==1)
  27. {
  28. OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
  29. OLED_WR_Byte(0xA0,OLED_CMD);
  30. }
  31. }
  32. //延时
  33. void IIC_delay(void)
  34. {
  35. // u8 t=10;
  36. // while(t--);
  37. delay_1us(5);
  38. }
  39. //起始信号
  40. void I2C_Start(void)
  41. {
  42. SDA_OUT();
  43. OLED_SDA_Set();
  44. OLED_SCL_Set();
  45. IIC_delay();
  46. OLED_SDA_Clr();
  47. IIC_delay();
  48. OLED_SCL_Clr();
  49. IIC_delay();
  50. }
  51. //结束信号
  52. void I2C_Stop(void)
  53. {
  54. SDA_OUT();
  55. OLED_SDA_Clr();
  56. OLED_SCL_Set();
  57. IIC_delay();
  58. OLED_SDA_Set();
  59. }
  60. //等待信号响应
  61. void I2C_WaitAck(void) //测数据信号的电平
  62. {
  63. OLED_SDA_Set();
  64. SDA_IN();
  65. IIC_delay();
  66. OLED_SCL_Set();
  67. IIC_delay();
  68. OLED_SCL_Clr();
  69. SDA_OUT();
  70. IIC_delay();
  71. }
  72. //写入一个字节
  73. void Send_Byte(u8 dat)
  74. {
  75. u8 i;
  76. SDA_OUT();
  77. for(i=0;i<8;i++)
  78. {
  79. if(dat&0x80)//将dat的8位从最高位依次写入
  80. {
  81. OLED_SDA_Set();
  82. }
  83. else
  84. {
  85. OLED_SDA_Clr();
  86. }
  87. IIC_delay();
  88. OLED_SCL_Set();
  89. IIC_delay();
  90. OLED_SCL_Clr();//将时钟信号设置为低电平
  91. dat<<=1;
  92. }
  93. }
  94. //发送一个字节
  95. //mode:数据/命令标志 0,表示命令;1,表示数据;
  96. void OLED_WR_Byte(u8 dat,u8 mode)
  97. {
  98. I2C_Start();
  99. Send_Byte(0x78);
  100. I2C_WaitAck();
  101. if(mode){Send_Byte(0x40);}
  102. else{Send_Byte(0x00);}
  103. I2C_WaitAck();
  104. Send_Byte(dat);
  105. I2C_WaitAck();
  106. I2C_Stop();
  107. }
  108. //开启OLED显示
  109. void OLED_DisPlay_On(void)
  110. {
  111. OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
  112. OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
  113. OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
  114. }
  115. //关闭OLED显示
  116. void OLED_DisPlay_Off(void)
  117. {
  118. OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
  119. OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
  120. OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
  121. }
  122. //更新显存到OLED
  123. void OLED_Refresh(void)
  124. {
  125. u8 i,n;
  126. for(i=0;i<8;i++)
  127. {
  128. OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
  129. OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
  130. OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
  131. I2C_Start();
  132. Send_Byte(0x78);
  133. I2C_WaitAck();
  134. Send_Byte(0x40);
  135. I2C_WaitAck();
  136. for(n=0;n<128;n++)
  137. {
  138. Send_Byte(OLED_GRAM[n][i]);
  139. I2C_WaitAck();
  140. }
  141. I2C_Stop();
  142. }
  143. }
  144. //清屏函数
  145. void OLED_Clear(void)
  146. {
  147. u8 i,n;
  148. for(i=0;i<8;i++)
  149. {
  150. for(n=0;n<128;n++)
  151. {
  152. OLED_GRAM[n][i]=0;//清除所有数据
  153. }
  154. }
  155. OLED_Refresh();//更新显示
  156. }
  157. //画点
  158. //x:0~127
  159. //y:0~63
  160. //t:1 填充 0,清空
  161. void OLED_DrawPoint(u8 x,u8 y,u8 t)
  162. {
  163. u8 i,m,n;
  164. i=y/8;
  165. m=y%8;
  166. n=1<<m;
  167. if(t){OLED_GRAM[x][i]|=n;}
  168. else
  169. {
  170. OLED_GRAM[x][i]=~OLED_GRAM[x][i];
  171. OLED_GRAM[x][i]|=n;
  172. OLED_GRAM[x][i]=~OLED_GRAM[x][i];
  173. }
  174. }
  175. //画线
  176. //x1,y1:起点坐标
  177. //x2,y2:结束坐标
  178. void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
  179. {
  180. u16 t;
  181. int xerr=0,yerr=0,delta_x,delta_y,distance;
  182. int incx,incy,uRow,uCol;
  183. delta_x=x2-x1; //计算坐标增量
  184. delta_y=y2-y1;
  185. uRow=x1;//画线起点坐标
  186. uCol=y1;
  187. if(delta_x>0)incx=1; //设置单步方向
  188. else if (delta_x==0)incx=0;//垂直线
  189. else {incx=-1;delta_x=-delta_x;}
  190. if(delta_y>0)incy=1;
  191. else if (delta_y==0)incy=0;//水平线
  192. else {incy=-1;delta_y=-delta_x;}
  193. if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴
  194. else distance=delta_y;
  195. for(t=0;t<distance+1;t++)
  196. {
  197. OLED_DrawPoint(uRow,uCol,mode);//画点
  198. xerr+=delta_x;
  199. yerr+=delta_y;
  200. if(xerr>distance)
  201. {
  202. xerr-=distance;
  203. uRow+=incx;
  204. }
  205. if(yerr>distance)
  206. {
  207. yerr-=distance;
  208. uCol+=incy;
  209. }
  210. }
  211. }
  212. //x,y:圆心坐标
  213. //r:圆的半径
  214. void OLED_DrawCircle(u8 x,u8 y,u8 r)
  215. {
  216. int a, b,num;
  217. a = 0;
  218. b = r;
  219. while(2 * b * b >= r * r)
  220. {
  221. OLED_DrawPoint(x + a, y - b,1);
  222. OLED_DrawPoint(x - a, y - b,1);
  223. OLED_DrawPoint(x - a, y + b,1);
  224. OLED_DrawPoint(x + a, y + b,1);
  225. OLED_DrawPoint(x + b, y + a,1);
  226. OLED_DrawPoint(x + b, y - a,1);
  227. OLED_DrawPoint(x - b, y - a,1);
  228. OLED_DrawPoint(x - b, y + a,1);
  229. a++;
  230. num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
  231. if(num > 0)
  232. {
  233. b--;
  234. a--;
  235. }
  236. }
  237. }
  238. //在指定位置显示一个字符,包括部分字符
  239. //x:0~127
  240. //y:0~63
  241. //size1:选择字体 6x8/6x12/8x16/12x24
  242. //mode:0,反色显示;1,正常显示
  243. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
  244. {
  245. u8 i,m,temp,size2,chr1;
  246. u8 x0=x,y0=y;
  247. if(size1==8)size2=6;
  248. else size2=(size1/8+((size1%8)?1:0))*(size1/2); //得到字体一个字符对应点阵集所占的字节数
  249. chr1=chr-' '; //计算偏移后的值
  250. for(i=0;i<size2;i++)
  251. {
  252. if(size1==8)
  253. {temp=asc2_0806[chr1][i];} //调用0806字体
  254. else if(size1==12)
  255. {temp=asc2_1206[chr1][i];} //调用1206字体
  256. else if(size1==16)
  257. {temp=asc2_1608[chr1][i];} //调用1608字体
  258. else if(size1==24)
  259. {temp=asc2_2412[chr1][i];} //调用2412字体
  260. else return;
  261. for(m=0;m<8;m++)
  262. {
  263. if(temp&0x01)OLED_DrawPoint(x,y,mode);
  264. else OLED_DrawPoint(x,y,!mode);
  265. temp>>=1;
  266. y++;
  267. }
  268. x++;
  269. if((size1!=8)&&((x-x0)==size1/2))
  270. {x=x0;y0=y0+8;}
  271. y=y0;
  272. }
  273. }
  274. //显示字符串
  275. //x,y:起点坐标
  276. //size1:字体大小
  277. //*chr:字符串起始地址
  278. //mode:0,反色显示;1,正常显示
  279. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
  280. {
  281. while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
  282. {
  283. OLED_ShowChar(x,y,*chr,size1,mode);
  284. if(size1==8)x+=6;
  285. else x+=size1/2;
  286. chr++;
  287. }
  288. }
  289. //m^n
  290. u32 OLED_Pow(u8 m,u8 n)
  291. {
  292. u32 result=1;
  293. while(n--)
  294. {
  295. result*=m;
  296. }
  297. return result;
  298. }
  299. //显示数字
  300. //x,y :起点坐标
  301. //num :要显示的数字
  302. //len :数字的位数
  303. //size:字体大小
  304. //mode:0,反色显示;1,正常显示
  305. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
  306. {
  307. u8 t,temp,m=0;
  308. if(size1==8)m=2;
  309. for(t=0;t<len;t++)
  310. {
  311. temp=(num/OLED_Pow(10,len-t-1))%10;
  312. if(temp==0)
  313. {
  314. OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
  315. }
  316. else
  317. {
  318. OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
  319. }
  320. }
  321. }
  322. //显示汉字
  323. //x,y:起点坐标
  324. //num:汉字对应的序号
  325. //mode:0,反色显示;1,正常显示
  326. void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
  327. {
  328. u8 m,temp;
  329. u8 x0=x,y0=y;
  330. u16 i,size3=(size1/8+((size1%8)?1:0))*size1; //得到字体一个字符对应点阵集所占的字节数
  331. for(i=0;i<size3;i++)
  332. {
  333. if(size1==16)
  334. {temp=Hzk1[num][i];}//调用16*16字体
  335. else if(size1==24)
  336. {temp=Hzk2[num][i];}//调用24*24字体
  337. else if(size1==32)
  338. {temp=Hzk3[num][i];}//调用32*32字体
  339. else if(size1==64)
  340. {temp=Hzk4[num][i];}//调用64*64字体
  341. else return;
  342. for(m=0;m<8;m++)
  343. {
  344. if(temp&0x01)OLED_DrawPoint(x,y,mode);
  345. else OLED_DrawPoint(x,y,!mode);
  346. temp>>=1;
  347. y++;
  348. }
  349. x++;
  350. if((x-x0)==size1)
  351. {x=x0;y0=y0+8;}
  352. y=y0;
  353. }
  354. }
  355. //num 显示汉字的个数
  356. //space 每一遍显示的间隔
  357. //mode:0,反色显示;1,正常显示
  358. void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
  359. {
  360. u8 i,n,t=0,m=0,r;
  361. while(1)
  362. {
  363. if(m==0)
  364. {
  365. OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
  366. t++;
  367. }
  368. if(t==num)
  369. {
  370. for(r=0;r<16*space;r++) //显示间隔
  371. {
  372. for(i=1;i<144;i++)
  373. {
  374. for(n=0;n<8;n++)
  375. {
  376. OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
  377. }
  378. }
  379. OLED_Refresh();
  380. }
  381. t=0;
  382. }
  383. m++;
  384. if(m==16){m=0;}
  385. for(i=1;i<144;i++) //实现左移
  386. {
  387. for(n=0;n<8;n++)
  388. {
  389. OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
  390. }
  391. }
  392. OLED_Refresh();
  393. }
  394. }
  395. //x,y:起点坐标
  396. //sizex,sizey,图片长宽
  397. //BMP[]:要写入的图片数组
  398. //mode:0,反色显示;1,正常显示
  399. void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
  400. {
  401. u16 j=0;
  402. u8 i,n,temp,m;
  403. u8 x0=x,y0=y;
  404. sizey=sizey/8+((sizey%8)?1:0);
  405. for(n=0;n<sizey;n++)
  406. {
  407. for(i=0;i<sizex;i++)
  408. {
  409. temp=BMP[j];
  410. j++;
  411. for(m=0;m<8;m++)
  412. {
  413. if(temp&0x01)OLED_DrawPoint(x,y,mode);
  414. else OLED_DrawPoint(x,y,!mode);
  415. temp>>=1;
  416. y++;
  417. }
  418. x++;
  419. if((x-x0)==sizex)
  420. {
  421. x=x0;
  422. y0=y0+8;
  423. }
  424. y=y0;
  425. }
  426. }
  427. }
  428. //OLED的初始化
  429. void OLED_Init(void)
  430. {
  431. GPIO_InitTypeDef GPIO_InitStructure;
  432. ////////////////// SCL
  433. RCC_AHB1PeriphClockCmd(SCL_PORT_RCC, ENABLE);
  434. //GPIO初始化设置
  435. GPIO_InitStructure.GPIO_Pin = SCL_PIN;
  436. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  437. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  438. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  439. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  440. GPIO_Init(SCL_PORT, &GPIO_InitStructure);//初始化
  441. ////////////////// SDA
  442. RCC_AHB1PeriphClockCmd(SDA_PORT_RCC, ENABLE);
  443. //GPIO初始化设置
  444. GPIO_InitStructure.GPIO_Pin = SDA_PIN;
  445. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输出模式
  446. GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  447. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  448. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  449. GPIO_Init(SDA_PORT, &GPIO_InitStructure);//初始化
  450. OLED_SCL_Clr();
  451. OLED_SDA_Clr();
  452. // OLED_RES_Clr();
  453. // delay_ms(200);
  454. // OLED_RES_Set();
  455. OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
  456. OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
  457. OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
  458. OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
  459. OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
  460. OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
  461. OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
  462. OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
  463. OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
  464. OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
  465. OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
  466. OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
  467. OLED_WR_Byte(0x00,OLED_CMD);//-not offset
  468. OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
  469. OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
  470. OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
  471. OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
  472. OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
  473. OLED_WR_Byte(0x12,OLED_CMD);
  474. OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
  475. OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
  476. OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
  477. OLED_WR_Byte(0x02,OLED_CMD);//
  478. OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
  479. OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
  480. OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
  481. OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
  482. OLED_Clear();
  483. OLED_WR_Byte(0xAF,OLED_CMD);
  484. }
  • 头文件的引入问题。处理#include "delay.h"替换为 #include "systick.h"
  • 修改delay函数

    void IIC_delay(void)
    {
    //    u8 t=10;
    //    while(t--);
          delay_1us(5);
    }
    

    I2C信号处理

    start信号
    void I2C_Start(void)
    {
      SDA_OUT();
    
      OLED_SDA_Set();
      OLED_SCL_Set();
      IIC_delay();
      OLED_SDA_Clr();
      IIC_delay();
      OLED_SCL_Clr();
      IIC_delay();
    }
    

    stop信号
    void I2C_Stop(void)
    {
      SDA_OUT();
    
      OLED_SDA_Clr();
      OLED_SCL_Set();
      IIC_delay();
      OLED_SDA_Set();
    }
    

    wait ack信号
    void I2C_WaitAck(void) //测数据信号的电平
    {
      OLED_SDA_Set();
      SDA_IN();
      IIC_delay();
      OLED_SCL_Set();
      IIC_delay();
      OLED_SCL_Clr();
      SDA_OUT();
      IIC_delay();
    }
    

    信号处理过程中,需要将输出改为输入,sda线需要由主从双方进行交替使用。

    头文件封装

    ```c

    define SDA_STATE() GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

define SDA_IN() { GPIO_InitTypeDef GPIO_InitStructure; \

                                        GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; \
                                        GPIO_Init(SDA_PORT, &GPIO_InitStructure);    }                                        

define SDA_OUT() { GPIO_InitTypeDef GPIO_InitStructure; \

                                        GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; \
                                        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; \
                                        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; \
                                        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; \
                                        GPIO_Init(SDA_PORT, &GPIO_InitStructure); }
<a name="NnaG2"></a>
#### 完整代码
```c
#ifndef __OLED_H
#define __OLED_H 

#include "stm32f4xx.h"
#include "stdlib.h"    

#ifndef u8
#define u8 uint8_t
#endif

#ifndef u16
#define u16 uint16_t
#endif

#ifndef u32
#define u32 uint32_t
#endif

#define SCL_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SCL_PORT            GPIOB
#define SCL_PIN                GPIO_Pin_6

#define SDA_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SDA_PORT            GPIOB
#define SDA_PIN                GPIO_Pin_7

#define SDA_STATE()     GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

#define SDA_IN()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure);    }                                        
#define SDA_OUT()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; \
                                            GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; \
                                            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; \
                                            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure); }
//#define SDA_IN()     0
//#define SDA_OUT() 0

//-----------------OLED端口定义---------------- 
#define OLED_SCL_Clr() GPIO_ResetBits(SCL_PORT,SCL_PIN)//SCL
#define OLED_SCL_Set() GPIO_SetBits(SCL_PORT,SCL_PIN)

#define OLED_SDA_Clr() GPIO_ResetBits(SDA_PORT,SDA_PIN)//DIN
#define OLED_SDA_Set() GPIO_SetBits(SDA_PORT,SDA_PIN)

//#define OLED_RES_Clr() GPIO_ResetBits(GPIOD,GPIO_Pin_4)//RES
//#define OLED_RES_Set() GPIO_SetBits(GPIOD,GPIO_Pin_4)


#define OLED_CMD  0    //写命令
#define OLED_DATA 1    //写数据

void OLED_ClearPoint(u8 x,u8 y);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
void OLED_DrawCircle(u8 x,u8 y,u8 r);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
void OLED_Init(void);

#endif
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"       
#include "systick.h"

u8 OLED_GRAM[144][8];

//反显函数
void OLED_ColorTurn(u8 i)
{
    if(i==0)
        {
            OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
        }
    if(i==1)
        {
            OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
        }
}

//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
    if(i==0)
        {
            OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
            OLED_WR_Byte(0xA1,OLED_CMD);
        }
    if(i==1)
        {
            OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
            OLED_WR_Byte(0xA0,OLED_CMD);
        }
}

//延时
void IIC_delay(void)
{
//    u8 t=10;
//    while(t--);
        delay_1us(5);
}

//起始信号
void I2C_Start(void)
{
    SDA_OUT();

    OLED_SDA_Set();
    OLED_SCL_Set();
    IIC_delay();
    OLED_SDA_Clr();
    IIC_delay();
    OLED_SCL_Clr();
    IIC_delay();
}

//结束信号
void I2C_Stop(void)
{
    SDA_OUT();

    OLED_SDA_Clr();
    OLED_SCL_Set();
    IIC_delay();
    OLED_SDA_Set();
}

//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
    OLED_SDA_Set();
    SDA_IN();
    IIC_delay();
    OLED_SCL_Set();
    IIC_delay();
    OLED_SCL_Clr();
    SDA_OUT();
    IIC_delay();
}

//写入一个字节
void Send_Byte(u8 dat)
{
    u8 i;
    SDA_OUT();

    for(i=0;i<8;i++)
    {
        if(dat&0x80)//将dat的8位从最高位依次写入
        {
            OLED_SDA_Set();
    }
        else
        {
            OLED_SDA_Clr();
    }
        IIC_delay();
        OLED_SCL_Set();
        IIC_delay();
        OLED_SCL_Clr();//将时钟信号设置为低电平
        dat<<=1;
  }
}

//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
    I2C_Start();
    Send_Byte(0x78);
    I2C_WaitAck();
    if(mode){Send_Byte(0x40);}
  else{Send_Byte(0x00);}
    I2C_WaitAck();
    Send_Byte(dat);
    I2C_WaitAck();
    I2C_Stop();
}

//开启OLED显示 
void OLED_DisPlay_On(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
    OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

//关闭OLED显示 
void OLED_DisPlay_Off(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
    OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}

//更新显存到OLED    
void OLED_Refresh(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
        OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
        OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
        I2C_Start();
        Send_Byte(0x78);
        I2C_WaitAck();
        Send_Byte(0x40);
        I2C_WaitAck();
        for(n=0;n<128;n++)
        {
            Send_Byte(OLED_GRAM[n][i]);
            I2C_WaitAck();
        }
        I2C_Stop();
  }
}
//清屏函数
void OLED_Clear(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
       for(n=0;n<128;n++)
            {
             OLED_GRAM[n][i]=0;//清除所有数据
            }
  }
    OLED_Refresh();//更新显示
}

//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空    
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
    u8 i,m,n;
    i=y/8;
    m=y%8;
    n=1<<m;
    if(t){OLED_GRAM[x][i]|=n;}
    else
    {
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
        OLED_GRAM[x][i]|=n;
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
    }
}

//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{
    u16 t; 
    int xerr=0,yerr=0,delta_x,delta_y,distance;
    int incx,incy,uRow,uCol;
    delta_x=x2-x1; //计算坐标增量 
    delta_y=y2-y1;
    uRow=x1;//画线起点坐标
    uCol=y1;
    if(delta_x>0)incx=1; //设置单步方向 
    else if (delta_x==0)incx=0;//垂直线 
    else {incx=-1;delta_x=-delta_x;}
    if(delta_y>0)incy=1;
    else if (delta_y==0)incy=0;//水平线 
    else {incy=-1;delta_y=-delta_x;}
    if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
    else distance=delta_y;
    for(t=0;t<distance+1;t++)
    {
        OLED_DrawPoint(uRow,uCol,mode);//画点
        xerr+=delta_x;
        yerr+=delta_y;
        if(xerr>distance)
        {
            xerr-=distance;
            uRow+=incx;
        }
        if(yerr>distance)
        {
            yerr-=distance;
            uCol+=incy;
        }
    }
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
    int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b,1);
        OLED_DrawPoint(x - a, y - b,1);
        OLED_DrawPoint(x - a, y + b,1);
        OLED_DrawPoint(x + a, y + b,1);

        OLED_DrawPoint(x + b, y + a,1);
        OLED_DrawPoint(x + b, y - a,1);
        OLED_DrawPoint(x - b, y - a,1);
        OLED_DrawPoint(x - b, y + a,1);

        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}



//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{
    u8 i,m,temp,size2,chr1;
    u8 x0=x,y0=y;
    if(size1==8)size2=6;
    else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
    chr1=chr-' ';  //计算偏移后的值
    for(i=0;i<size2;i++)
    {
        if(size1==8)
              {temp=asc2_0806[chr1][i];} //调用0806字体
        else if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
        else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
        else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
        else return;
        for(m=0;m<8;m++)
        {
            if(temp&0x01)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp>>=1;
            y++;
        }
        x++;
        if((size1!=8)&&((x-x0)==size1/2))
        {x=x0;y0=y0+8;}
        y=y0;
  }
}


//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{
    while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
    {
        OLED_ShowChar(x,y,*chr,size1,mode);
        if(size1==8)x+=6;
        else x+=size1/2;
        chr++;
  }
}

//m^n
u32 OLED_Pow(u8 m,u8 n)
{
    u32 result=1;
    while(n--)
    {
      result*=m;
    }
    return result;
}

//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{
    u8 t,temp,m=0;
    if(size1==8)m=2;
    for(t=0;t<len;t++)
    {
        temp=(num/OLED_Pow(10,len-t-1))%10;
            if(temp==0)
            {
                OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
      }
            else 
            {
              OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
            }
  }
}

//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{
    u8 m,temp;
    u8 x0=x,y0=y;
    u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数
    for(i=0;i<size3;i++)
    {
        if(size1==16)
                {temp=Hzk1[num][i];}//调用16*16字体
        else if(size1==24)
                {temp=Hzk2[num][i];}//调用24*24字体
        else if(size1==32)       
                {temp=Hzk3[num][i];}//调用32*32字体
        else if(size1==64)
                {temp=Hzk4[num][i];}//调用64*64字体
        else return;
        for(m=0;m<8;m++)
        {
            if(temp&0x01)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp>>=1;
            y++;
        }
        x++;
        if((x-x0)==size1)
        {x=x0;y0=y0+8;}
        y=y0;
    }
}

//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{
    u8 i,n,t=0,m=0,r;
    while(1)
    {
        if(m==0)
        {
        OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
            t++;
        }
        if(t==num)
            {
                for(r=0;r<16*space;r++)      //显示间隔
                 {
                    for(i=1;i<144;i++)
                        {
                            for(n=0;n<8;n++)
                            {
                                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
                            }
                        }
           OLED_Refresh();
                 }
        t=0;
      }
        m++;
        if(m==16){m=0;}
        for(i=1;i<144;i++)   //实现左移
        {
            for(n=0;n<8;n++)
            {
                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
            }
        }
        OLED_Refresh();
    }
}

//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{
    u16 j=0;
    u8 i,n,temp,m;
    u8 x0=x,y0=y;
    sizey=sizey/8+((sizey%8)?1:0);
    for(n=0;n<sizey;n++)
    {
         for(i=0;i<sizex;i++)
         {
                temp=BMP[j];
                j++;
                for(m=0;m<8;m++)
                {
                    if(temp&0x01)OLED_DrawPoint(x,y,mode);
                    else OLED_DrawPoint(x,y,!mode);
                    temp>>=1;
                    y++;
                }
                x++;
                if((x-x0)==sizex)
                {
                    x=x0;
                    y0=y0+8;
                }
                y=y0;
     }
     }
}
//OLED的初始化
void OLED_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

    ////////////////// SCL
    RCC_AHB1PeriphClockCmd(SCL_PORT_RCC, ENABLE);
    //GPIO初始化设置
  GPIO_InitStructure.GPIO_Pin = SCL_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  GPIO_Init(SCL_PORT, &GPIO_InitStructure);//初始化

    ////////////////// SDA
    RCC_AHB1PeriphClockCmd(SDA_PORT_RCC, ENABLE);
    //GPIO初始化设置
  GPIO_InitStructure.GPIO_Pin = SDA_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
  GPIO_Init(SDA_PORT, &GPIO_InitStructure);//初始化

    OLED_SCL_Clr();
    OLED_SDA_Clr();
//    OLED_RES_Clr();
//    delay_ms(200);
//    OLED_RES_Set();

    OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
    OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
    OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
    OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
    OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
    OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
    OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
    OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
    OLED_WR_Byte(0x00,OLED_CMD);//-not offset
    OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
    OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
    OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
    OLED_WR_Byte(0x12,OLED_CMD);
    OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
    OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
    OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_WR_Byte(0x02,OLED_CMD);//
    OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
    OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
    OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
    OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
    OLED_Clear();
    OLED_WR_Byte(0xAF,OLED_CMD);
}

STM32F4软件I2C封装

I2C封装

#ifndef __SOFT_I2C0_H__
#define __SOFT_I2C0_H__

#include "stm32f4xx.h"
#include "systick.h"


#define SCL_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SCL_PORT            GPIOB
#define SCL_PIN                GPIO_Pin_6

#define SDA_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SDA_PORT            GPIOB
#define SDA_PIN                GPIO_Pin_7


#define SCL(BIT)             GPIO_WriteBit(SCL_PORT, SCL_PIN, BIT?SET:RESET)
#define SDA(BIT)             GPIO_WriteBit(SDA_PORT, SDA_PIN, BIT?SET:RESET)
#define SDA_STATE()     GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

#define SDA_IN()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure);    }                                        
#define SDA_OUT()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; \
                                            GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; \
                                            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; \
                                            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure); }


void SoftI2C0_init();
uint8_t SoftI2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
uint8_t SoftI2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);

uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len);

#endif
#include "Soft_I2C0.h"


static void start();
static void stop();
static void send(uint8_t data);
static uint8_t recv();
static uint8_t wait_ack();
static void send_ack();
static void send_nack(); 


void SoftI2C0_init() {
        GPIO_InitTypeDef  GPIO_InitStructure;

        ////////////////// SCL
        RCC_AHB1PeriphClockCmd(SCL_PORT_RCC, ENABLE);
        //GPIO初始化设置
        GPIO_InitStructure.GPIO_Pin = SCL_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
        GPIO_Init(SCL_PORT, &GPIO_InitStructure);//初始化

        ////////////////// SDA
        RCC_AHB1PeriphClockCmd(SDA_PORT_RCC, ENABLE);
        //GPIO初始化设置
        GPIO_InitStructure.GPIO_Pin = SDA_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输出模式
        GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
        GPIO_Init(SDA_PORT, &GPIO_InitStructure);//初始化
}

uint8_t SoftI2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);            //发送设备写地址
    if(wait_ack()) return 1;    //等待响应

    send(reg);                    //发送寄存器地址
    if(wait_ack()) return 2;    //等待响应

    do {
        send(*data++);        
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {
        start();

    send(addr << 1);            //发送设备写地址
    if(wait_ack()) return 1;    //等待响应

    send(reg);                    //发送寄存器地址
    if(wait_ack()) return 2;    //等待响应

    do {
        send(*data);
                data += offset;
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t SoftI2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);                //发送设备写地址
    if(wait_ack()) return 1;        //等待响应

    send(reg);                        //发送寄存器地址
    if(wait_ack()) return 2;        //等待响应

    start();
    send((addr << 1) | 0x01);        //发送设备读地址
    if(wait_ack()) return 3;        //等待响应

    do {
        *data = recv();
        data++;
        if(len != 1) send_ack();    // 发送 NACK
    } while(--len);
    send_nack();                    // 发送 NACK
    stop();

    return 0;
}

static void start() {
    SDA_OUT();

    SDA(1);
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    SDA(0);
    delay_1us(5);
    SCL(0);
    delay_1us(5);
}

static void stop() {
    SDA_OUT();

    SCL(0);
    SDA(0);

    SCL(1);
    delay_1us(5);
    SDA(1);
    delay_1us(5);
}

static void send(uint8_t data) {
    uint8_t i;
    SDA_OUT();

    for(i = 0; i < 8; i++) {
        if(data & 0x80) {
            SDA(1);
        } else {
            SDA(0);
        }
        SCL(1);
        delay_1us(5);
        SCL(0);
        delay_1us(5);
        data <<= 1;
    }
}

static uint8_t recv() {
    uint8_t i, data;
    SDA_IN();
    data = 0;
    for(i = 0; i < 8; i++) {
        SCL(0);
        delay_1us(5);
        SCL(1);
        delay_1us(5);

        data <<= 1;

        data |= SDA_STATE();

        delay_1us(5);
    }
    SCL(0);
    return data;
}

static uint8_t wait_ack() {
    int8_t retry = 10;

    SCL(0);
    SDA(1);
    SDA_IN();
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    while(SDA_STATE() == 1 && retry > 0) {
        retry --;
        delay_1us(5);
    }

    if(retry <= 0) {
        stop();
        return 1;
    } else {
        SCL(0);
        SDA_OUT();
    }
    return 0;
}

static void send_ack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(0);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}

static void send_nack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(1);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}

驱动加减法

#ifndef __OLED_H
#define __OLED_H 

#include "stm32f4xx.h"
#include "stdlib.h"    
#include "Soft_I2C0.h"

#ifndef u8
#define u8 uint8_t
#endif

#ifndef u16
#define u16 uint16_t
#endif

#ifndef u32
#define u32 uint32_t
#endif


#define OLED_CMD  0    //写命令
#define OLED_DATA 1    //写数据

void OLED_ClearPoint(u8 x,u8 y);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
//void I2C_Start(void);
//void I2C_Stop(void);
//void I2C_WaitAck(void);
//void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
void OLED_DrawCircle(u8 x,u8 y,u8 r);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
void OLED_Init(void);

#endif
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"       
#include "systick.h"

u8 OLED_GRAM[144][8];

//反显函数
void OLED_ColorTurn(u8 i)
{
    if(i==0)
        {
            OLED_WR_Byte(0xA6,OLED_CMD);//正常显示
        }
    if(i==1)
        {
            OLED_WR_Byte(0xA7,OLED_CMD);//反色显示
        }
}

//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{
    if(i==0)
        {
            OLED_WR_Byte(0xC8,OLED_CMD);//正常显示
            OLED_WR_Byte(0xA1,OLED_CMD);
        }
    if(i==1)
        {
            OLED_WR_Byte(0xC0,OLED_CMD);//反转显示
            OLED_WR_Byte(0xA0,OLED_CMD);
        }
}

//延时
void IIC_delay(void)
{
//    u8 t=10;
//    while(t--);
        delay_1us(5);
}

////起始信号
//void I2C_Start(void)
//{
//    SDA_OUT();
//    
//    OLED_SDA_Set();
//    OLED_SCL_Set();
//    IIC_delay();
//    OLED_SDA_Clr();
//    IIC_delay();
//    OLED_SCL_Clr();
//    IIC_delay();
//}

////结束信号
//void I2C_Stop(void)
//{
//    SDA_OUT();
//    
//    OLED_SDA_Clr();
//    OLED_SCL_Set();
//    IIC_delay();
//    OLED_SDA_Set();
//}

////等待信号响应
//void I2C_WaitAck(void) //测数据信号的电平
//{
//    OLED_SDA_Set();
//    SDA_IN();
//    IIC_delay();
//    OLED_SCL_Set();
//    IIC_delay();
//    OLED_SCL_Clr();
//    SDA_OUT();
//    IIC_delay();
//}

////写入一个字节
//void Send_Byte(u8 dat)
//{
//    u8 i;
//    SDA_OUT();
//    
//    for(i=0;i<8;i++)
//    {
//        if(dat&0x80)//将dat的8位从最高位依次写入
//        {
//            OLED_SDA_Set();
//    }
//        else
//        {
//            OLED_SDA_Clr();
//    }
//        IIC_delay();
//        OLED_SCL_Set();
//        IIC_delay();
//        OLED_SCL_Clr();//将时钟信号设置为低电平
//        dat<<=1;
//  }
//}

//发送一个字节
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat,u8 mode)
{
//    I2C_Start();
//    Send_Byte(0x78);
//    I2C_WaitAck();
//    if(mode){Send_Byte(0x40);}
//  else{Send_Byte(0x00);}
//    I2C_WaitAck();
//    Send_Byte(dat);
//    I2C_WaitAck();
//    I2C_Stop();
        if(mode) {
                SoftI2C0_write(0x78 >> 1, 0x40, &dat, 1);
        } else {
                SoftI2C0_write(0x78 >> 1, 0x00, &dat, 1);
        }
}

//开启OLED显示 
void OLED_DisPlay_On(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
    OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

//关闭OLED显示 
void OLED_DisPlay_Off(void)
{
    OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
    OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}

//更新显存到OLED    
void OLED_Refresh(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
        OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
        OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
        OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
//        I2C_Start();
//        Send_Byte(0x78);
//        I2C_WaitAck();
//        Send_Byte(0x40);
//        I2C_WaitAck();
//        for(n=0;n<128;n++)
//        {
//            Send_Byte(OLED_GRAM[n][i]);
//            I2C_WaitAck();
//        }
//        I2C_Stop();

        SoftI2C0_write2(0x78 >> 1, 0x40, &(OLED_GRAM[0][i]), 8, 128);
  }
}
//清屏函数
void OLED_Clear(void)
{
    u8 i,n;
    for(i=0;i<8;i++)
    {
       for(n=0;n<128;n++)
            {
             OLED_GRAM[n][i]=0;//清除所有数据
            }
  }
    OLED_Refresh();//更新显示
}

//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空    
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
    u8 i,m,n;
    i=y/8;
    m=y%8;
    n=1<<m;
    if(t){OLED_GRAM[x][i]|=n;}
    else
    {
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
        OLED_GRAM[x][i]|=n;
        OLED_GRAM[x][i]=~OLED_GRAM[x][i];
    }
}

//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{
    u16 t; 
    int xerr=0,yerr=0,delta_x,delta_y,distance;
    int incx,incy,uRow,uCol;
    delta_x=x2-x1; //计算坐标增量 
    delta_y=y2-y1;
    uRow=x1;//画线起点坐标
    uCol=y1;
    if(delta_x>0)incx=1; //设置单步方向 
    else if (delta_x==0)incx=0;//垂直线 
    else {incx=-1;delta_x=-delta_x;}
    if(delta_y>0)incy=1;
    else if (delta_y==0)incy=0;//水平线 
    else {incy=-1;delta_y=-delta_x;}
    if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
    else distance=delta_y;
    for(t=0;t<distance+1;t++)
    {
        OLED_DrawPoint(uRow,uCol,mode);//画点
        xerr+=delta_x;
        yerr+=delta_y;
        if(xerr>distance)
        {
            xerr-=distance;
            uRow+=incx;
        }
        if(yerr>distance)
        {
            yerr-=distance;
            uCol+=incy;
        }
    }
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{
    int a, b,num;
    a = 0;
    b = r;
    while(2 * b * b >= r * r)      
    {
        OLED_DrawPoint(x + a, y - b,1);
        OLED_DrawPoint(x - a, y - b,1);
        OLED_DrawPoint(x - a, y + b,1);
        OLED_DrawPoint(x + a, y + b,1);

        OLED_DrawPoint(x + b, y + a,1);
        OLED_DrawPoint(x + b, y - a,1);
        OLED_DrawPoint(x - b, y - a,1);
        OLED_DrawPoint(x - b, y + a,1);

        a++;
        num = (a * a + b * b) - r*r;//计算画的点离圆心的距离
        if(num > 0)
        {
            b--;
            a--;
        }
    }
}



//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{
    u8 i,m,temp,size2,chr1;
    u8 x0=x,y0=y;
    if(size1==8)size2=6;
    else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
    chr1=chr-' ';  //计算偏移后的值
    for(i=0;i<size2;i++)
    {
        if(size1==8)
              {temp=asc2_0806[chr1][i];} //调用0806字体
        else if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
        else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
        else if(size1==24)
        {temp=asc2_2412[chr1][i];} //调用2412字体
        else return;
        for(m=0;m<8;m++)
        {
            if(temp&0x01)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp>>=1;
            y++;
        }
        x++;
        if((size1!=8)&&((x-x0)==size1/2))
        {x=x0;y0=y0+8;}
        y=y0;
  }
}


//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{
    while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
    {
        OLED_ShowChar(x,y,*chr,size1,mode);
        if(size1==8)x+=6;
        else x+=size1/2;
        chr++;
  }
}

//m^n
u32 OLED_Pow(u8 m,u8 n)
{
    u32 result=1;
    while(n--)
    {
      result*=m;
    }
    return result;
}

//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{
    u8 t,temp,m=0;
    if(size1==8)m=2;
    for(t=0;t<len;t++)
    {
        temp=(num/OLED_Pow(10,len-t-1))%10;
            if(temp==0)
            {
                OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
      }
            else 
            {
              OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
            }
  }
}

//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{
    u8 m,temp;
    u8 x0=x,y0=y;
    u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数
    for(i=0;i<size3;i++)
    {
        if(size1==16)
                {temp=Hzk1[num][i];}//调用16*16字体
        else if(size1==24)
                {temp=Hzk2[num][i];}//调用24*24字体
        else if(size1==32)       
                {temp=Hzk3[num][i];}//调用32*32字体
        else if(size1==64)
                {temp=Hzk4[num][i];}//调用64*64字体
        else return;
        for(m=0;m<8;m++)
        {
            if(temp&0x01)OLED_DrawPoint(x,y,mode);
            else OLED_DrawPoint(x,y,!mode);
            temp>>=1;
            y++;
        }
        x++;
        if((x-x0)==size1)
        {x=x0;y0=y0+8;}
        y=y0;
    }
}

//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{
    u8 i,n,t=0,m=0,r;
    while(1)
    {
        if(m==0)
        {
        OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
            t++;
        }
        if(t==num)
            {
                for(r=0;r<16*space;r++)      //显示间隔
                 {
                    for(i=1;i<144;i++)
                        {
                            for(n=0;n<8;n++)
                            {
                                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
                            }
                        }
           OLED_Refresh();
                 }
        t=0;
      }
        m++;
        if(m==16){m=0;}
        for(i=1;i<144;i++)   //实现左移
        {
            for(n=0;n<8;n++)
            {
                OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
            }
        }
        OLED_Refresh();
    }
}

//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{
    u16 j=0;
    u8 i,n,temp,m;
    u8 x0=x,y0=y;
    sizey=sizey/8+((sizey%8)?1:0);
    for(n=0;n<sizey;n++)
    {
         for(i=0;i<sizex;i++)
         {
                temp=BMP[j];
                j++;
                for(m=0;m<8;m++)
                {
                    if(temp&0x01)OLED_DrawPoint(x,y,mode);
                    else OLED_DrawPoint(x,y,!mode);
                    temp>>=1;
                    y++;
                }
                x++;
                if((x-x0)==sizex)
                {
                    x=x0;
                    y0=y0+8;
                }
                y=y0;
     }
     }
}
//OLED的初始化
void OLED_Init(void)
{
//    OLED_RES_Clr();
//    delay_ms(200);
//    OLED_RES_Set();

    OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
    OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
    OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
    OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
    OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness
    OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
    OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
    OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
    OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
    OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset    Shift Mapping RAM Counter (0x00~0x3F)
    OLED_WR_Byte(0x00,OLED_CMD);//-not offset
    OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
    OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
    OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
    OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
    OLED_WR_Byte(0x12,OLED_CMD);
    OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
    OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
    OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
    OLED_WR_Byte(0x02,OLED_CMD);//
    OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
    OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
    OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
    OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) 
    OLED_Clear();
    OLED_WR_Byte(0xAF,OLED_CMD);
}

去掉I2C处理的逻辑,将I2C和驱动分离,原因是,在整个系统中,I2C是总线,会外挂多个设备,不应该将一个设备和I2C绑死。

二维数组

uint8_t arr[4][5] = {
    {0, 1, 2, 3, 4},
    {10, 11, 12, 13, 14},
    {20, 21, 22, 23, 24},
    {30, 31, 32, 33, 34}
}

二维数组可以理解为一张表,上面的数组对应下面的内容:

0 1 2 3 4
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34

在内存中的存储也是连续的,存储方式如下:

0 1 2 3 4 10 11 12 13 14 20 21 22 23 24 30 31 32 33 34

内存中是一行一行的进行存储的。
那么如果我想获得一个第1行第2列的值,可以如下方式获取:

arr[1][2];

如果想获得地址,也是一样的:

&arr[1][2];

如果我把第1行第2列这个指针给到你,我们该如何它对应的取值:

void current(uint8_t* p) {
    uint8_t data = *p;
}

current(&arr[1][2]);

如果我把第1行第2列这个指针给到你,我们该如何它下一行这个位置的值:

void next(uint8_t* p) {
    p += 5;
    uint8_t data = *p;
}
next(&arr[1][2]);

功能扩展

uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {
    start();

    send(addr << 1);            //发送设备写地址
    if(wait_ack()) return 1;    //等待响应

    send(reg);                    //发送寄存器地址
    if(wait_ack()) return 2;    //等待响应

    do {
        send(*data);
        data += offset;
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

由于OLED屏幕内部维护了一个二维数组,二维数组又是按照列去发送数据的,因此我们对数据发送逻辑进行修改操作。

测试

SoftI2C0_init();

u8 t=' ';
OLED_Init();

while(1) {
    OLED_ShowPicture(0,0,128,64,BMP1,1);
    OLED_Refresh();
    delay_1ms(500);
    OLED_Clear();
    OLED_ShowChinese(0,0,0,16,1);//中
    OLED_ShowChinese(18,0,1,16,1);//景
    OLED_ShowChinese(36,0,2,16,1);//园
    OLED_ShowChinese(54,0,3,16,1);//电
    OLED_ShowChinese(72,0,4,16,1);//子
    OLED_ShowChinese(90,0,5,16,1);//技
    OLED_ShowChinese(108,0,6,16,1);//术
    OLED_ShowString(8,16,"ZHONGJINGYUAN",16,1);
    OLED_ShowString(20,32,"2014/05/01",16,1);
    OLED_ShowString(0,48,"ASCII:",16,1);
    OLED_ShowString(63,48,"CODE:",16,1);
    OLED_ShowChar(48,48,t,16,1);//显示ASCII字符
    t++;
    if(t>'~')t=' ';
    OLED_ShowNum(103,48,t,3,16,1);
    OLED_Refresh();
    delay_1ms(500);
    OLED_Clear();
    OLED_ShowChinese(0,0,0,16,1);  //16*16 中
    OLED_ShowChinese(16,0,0,24,1); //24*24 中
    OLED_ShowChinese(24,20,0,32,1);//32*32 中
    OLED_ShowChinese(64,0,0,64,1); //64*64 中
    OLED_Refresh();
    delay_1ms(500);
    OLED_Clear();
    OLED_ShowString(0,0,"ABC",8,1);//6*8 “ABC”
    OLED_ShowString(0,8,"ABC",12,1);//6*12 “ABC”
    OLED_ShowString(0,20,"ABC",16,1);//8*16 “ABC”
    OLED_ShowString(0,36,"ABC",24,1);//12*24 “ABC”
    OLED_Refresh();
    delay_1ms(500);
    OLED_ScrollDisplay(11,4,1);
}

完整代码

I2C驱动代码

#include "Soft_I2C0.h"


static void start();
static void stop();
static void send(uint8_t data);
static uint8_t recv();
static uint8_t wait_ack();
static void send_ack();
static void send_nack(); 


void SoftI2C0_init() {
    GPIO_InitTypeDef  GPIO_InitStructure;

    ////////////////// SCL
    RCC_AHB1PeriphClockCmd(SCL_PORT_RCC, ENABLE);
    //GPIO初始化设置
    GPIO_InitStructure.GPIO_Pin = SCL_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
    GPIO_Init(SCL_PORT, &GPIO_InitStructure);//初始化

    ////////////////// SDA
    RCC_AHB1PeriphClockCmd(SDA_PORT_RCC, ENABLE);
    //GPIO初始化设置
    GPIO_InitStructure.GPIO_Pin = SDA_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉
    GPIO_Init(SDA_PORT, &GPIO_InitStructure);//初始化
}

uint8_t SoftI2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);            //发送设备写地址
    if(wait_ack()) return 1;    //等待响应

    send(reg);                    //发送寄存器地址
    if(wait_ack()) return 2;    //等待响应

    do {
        send(*data++);        
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len) {
    start();

    send(addr << 1);            //发送设备写地址
    if(wait_ack()) return 1;    //等待响应

    send(reg);                    //发送寄存器地址
    if(wait_ack()) return 2;    //等待响应

    do {
        send(*data);
        data += offset;
        if(wait_ack()) return 3;
    } while(--len);

    stop();
    return 0;
}

uint8_t SoftI2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len) {
    start();

    send(addr << 1);                //发送设备写地址
    if(wait_ack()) return 1;        //等待响应

    send(reg);                        //发送寄存器地址
    if(wait_ack()) return 2;        //等待响应

    start();
    send((addr << 1) | 0x01);        //发送设备读地址
    if(wait_ack()) return 3;        //等待响应

    do {
        *data = recv();
        data++;
        if(len != 1) send_ack();    // 发送 NACK
    } while(--len);
    send_nack();                    // 发送 NACK
    stop();

    return 0;
}

static void start() {
    SDA_OUT();

    SDA(1);
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    SDA(0);
    delay_1us(5);
    SCL(0);
    delay_1us(5);
}

static void stop() {
    SDA_OUT();

    SCL(0);
    SDA(0);

    SCL(1);
    delay_1us(5);
    SDA(1);
    delay_1us(5);
}

static void send(uint8_t data) {
    uint8_t i;
    SDA_OUT();

    for(i = 0; i < 8; i++) {
        if(data & 0x80) {
            SDA(1);
        } else {
            SDA(0);
        }
        SCL(1);
        delay_1us(5);
        SCL(0);
        delay_1us(5);
        data <<= 1;
    }
}

static uint8_t recv() {
    uint8_t i, data;
    SDA_IN();
    data = 0;
    for(i = 0; i < 8; i++) {
        SCL(0);
        delay_1us(5);
        SCL(1);
        delay_1us(5);

        data <<= 1;

        data |= SDA_STATE();

        delay_1us(5);
    }
    SCL(0);
    return data;
}

static uint8_t wait_ack() {
    int8_t retry = 10;

    SCL(0);
    SDA(1);
    SDA_IN();
    delay_1us(5);
    SCL(1);
    delay_1us(5);

    while(SDA_STATE() == 1 && retry > 0) {
        retry --;
        delay_1us(5);
    }

    if(retry <= 0) {
        stop();
        return 1;
    } else {
        SCL(0);
        SDA_OUT();
    }
    return 0;
}

static void send_ack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(0);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}

static void send_nack() {
    SDA_OUT();
    SCL(0);
    SDA(0);
    delay_1us(5);

    SDA(1);

    SCL(1);
    delay_1us(5);
    SCL(0);
    SDA(1);
}

STM32F4
#ifndef __SOFT_I2C0_H__
#define __SOFT_I2C0_H__

#include "stm32f4xx.h"
#include "systick.h"


#define SCL_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SCL_PORT            GPIOB
#define SCL_PIN                GPIO_Pin_6

#define SDA_PORT_RCC    RCC_AHB1Periph_GPIOB
#define SDA_PORT            GPIOB
#define SDA_PIN                GPIO_Pin_7


#define SCL(BIT)             GPIO_WriteBit(SCL_PORT, SCL_PIN, BIT?SET:RESET)
#define SDA(BIT)             GPIO_WriteBit(SDA_PORT, SDA_PIN, BIT?SET:RESET)
#define SDA_STATE()     GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN)

#define SDA_IN()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure);    }                                        
#define SDA_OUT()        {    GPIO_InitTypeDef  GPIO_InitStructure;    \
                                            GPIO_InitStructure.GPIO_Pin = SDA_PIN; \
                                            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; \
                                            GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; \
                                            GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; \
                                            GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; \
                                            GPIO_Init(SDA_PORT, &GPIO_InitStructure); }


void SoftI2C0_init();
uint8_t SoftI2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
uint8_t SoftI2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);

uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len);

#endif

GD32F4
#ifndef __SOFT_I2C0_H__
#define __SOFT_I2C0_H__

#include "gd32f4xx.h"
#include "systick.h"


#define SCL_RCU        RCU_GPIOB
#define SCL_PORT    GPIOB
#define SCL_PIN        GPIO_PIN_6
#define SCL_AF        GPIO_AF_4

#define SDA_RCU        RCU_GPIOB
#define SDA_PORT    GPIOB
#define SDA_PIN        GPIO_PIN_7
#define SDA_AF        GPIO_AF_4

/************** io ***************/
#define SCL(BIT)             gpio_bit_write(SCL_PORT, SCL_PIN, BIT?SET:RESET)
#define SDA(BIT)             gpio_bit_write(SDA_PORT, SDA_PIN, BIT?SET:RESET)
#define SDA_STATE()     gpio_input_bit_get(SDA_PORT, SDA_PIN)

#define SDA_IN()          gpio_mode_set(SDA_PORT, GPIO_MODE_INPUT,  GPIO_PUPD_NONE, SDA_PIN)
#define SDA_OUT()         gpio_mode_set(SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SDA_PIN)


void SoftI2C0_init();
uint8_t SoftI2C0_read(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
uint8_t SoftI2C0_write(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t len);
uint8_t SoftI2C0_write2(uint8_t addr, uint8_t reg, uint8_t* data, uint32_t offset, uint32_t len);


#endif

练习题

  1. 实现软件i2c显示
  2. 实现硬件i2c显示