查看: 1549|回复: 0
收起左侧

第六十八章:CH32V103应用教程——IIC-模拟IIC驱动OLED

[复制链接]

  离线 

  • TA的每日心情
    慵懒
    2021-7-23 17:16
  • 签到天数: 17 天

    [LV.4]

    发表于 2021-4-30 20:36:05 | 显示全部楼层 |阅读模式

    有人预言,RISC-V或将是继Intel和Arm之后的第三大主流处理器体系。欢迎访问全球首家只专注于RISC-V单片机行业应用的中文网站

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    本帖最后由 草帽王子 于 2021-9-10 18:00 编辑

    本章教程主要在第三十七章软件模拟IIC读写EEPROM的基础上进行改动,使用模拟IIC驱动OLED屏。


    1、IIC简介及相关函数介绍

    关于IIC,在前面第十四章以及第三十七章已进行过介绍,在此不再赘述。


    2、硬件设计

    本章教程主要使用模拟IIC驱动OLED屏,所用OLED屏为0.96寸OLED屏。程序中配置PA1作为SDA线,PA2作为SCL线,具体连接方式如下:
    • PA1连接OLED屏的SDA引脚
    • PA2连接OLED屏的SCL引脚


    3、软件设计

    关于模拟IIC的初始化配置以及各状态的配置,在前面第三十七章已经介绍,在此不再赘述。本章教程主要对OLED相关配置文件进行介绍,具体如下:
    oled.h文件
    1. #ifndef __OLED_H
    2. #define __OLED_H

    3. #include "ch32v10x.h"
    4. #include "ch32v10x_conf.h"

    5. #define OLED_CMD  0
    6. #define OLED_DATA 1

    7. void OLED_ClearPoint(u8 x,u8 y);
    8. void OLED_ColorTurn(u8 i);
    9. void OLED_DisplayTurn(u8 i);
    10. void OLED_WR_Byte(u8 dat,u8 mode);
    11. void OLED_DisPlay_On(void);
    12. void OLED_DisPlay_Off(void);
    13. void OLED_Refresh(void);
    14. void OLED_Clear(void);
    15. void OLED_DrawPoint(u8 x,u8 y,u8 t);
    16. void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
    17. void OLED_DrawCircle(u8 x,u8 y,u8 r);
    18. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
    19. void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
    20. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode);
    21. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
    22. void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
    23. void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
    24. void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
    25. void OLED_Init(void);

    26. #endif
    复制代码
    oled.h文件主要进行相关定义和函数声明;
    oled.c文件
    1. #include "oled.h"
    2. #include "oledfont.h"
    3. #include "iic.h"
    4. #include "debug.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. //mode:数据/命令标志  0,表示命令;1,表示数据;
    34. void OLED_WR_Byte(u8 dat,u8 mode)
    35. {
    36.     IIC_Start();
    37.     IIC_SendByte(0x78);
    38.     IIC_WaitAck();
    39.     if(mode){IIC_SendByte(0x40);}
    40.     else{IIC_SendByte(0x00);}
    41.     IIC_WaitAck();
    42.     IIC_SendByte(dat);
    43.     IIC_WaitAck();
    44.     IIC_Stop();
    45. }

    46. //开启OLED显示
    47. void OLED_DisPlay_On(void)
    48. {
    49.     OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    50.     OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
    51.     OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
    52. }

    53. //关闭OLED显示
    54. void OLED_DisPlay_Off(void)
    55. {
    56.     OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
    57.     OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
    58.     OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
    59. }

    60. //更新显存到OLED
    61. void OLED_Refresh(void)
    62. {
    63.     u8 i,n;
    64.     for(i=0;i<8;i++)
    65.     {
    66.         OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
    67.         OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
    68.         OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
    69.         IIC_Start();
    70.         IIC_SendByte(0x78);
    71.         IIC_WaitAck();
    72.         IIC_SendByte(0x40);
    73.         IIC_WaitAck();
    74.         for(n=0;n<128;n++)
    75.         {
    76.             IIC_SendByte(OLED_GRAM[n][i]);
    77.             IIC_WaitAck();
    78.         }
    79.         IIC_Stop();
    80.   }
    81. }

    82. //清屏函数
    83. void OLED_Clear(void)
    84. {
    85.     u8 i,n;
    86.     for(i=0;i<8;i++)
    87.     {
    88.        for(n=0;n<128;n++)
    89.             {
    90.              OLED_GRAM[n][i]=0;//清除所有数据
    91.             }
    92.   }
    93.     OLED_Refresh();            //更新显示
    94. }

    95. //画点
    96. //x:0~127
    97. //y:0~63
    98. //t:1,填充 ;0,清空
    99. void OLED_DrawPoint(u8 x,u8 y,u8 t)
    100. {
    101.     u8 i,m,n;
    102.     i=y/8;
    103.     m=y%8;
    104.     n=1<<m;
    105.     if(t){OLED_GRAM[x][i]|=n;}
    106.     else
    107.     {
    108.         OLED_GRAM[x][i]=~OLED_GRAM[x][i];
    109.         OLED_GRAM[x][i]|=n;
    110.         OLED_GRAM[x][i]=~OLED_GRAM[x][i];
    111.     }
    112. }

    113. //画线
    114. //x1,y1:起点坐标
    115. //x2,y2:结束坐标
    116. void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
    117. {
    118.     u16 t;
    119.     int xerr=0,yerr=0,delta_x,delta_y,distance;
    120.     int incx,incy,uRow,uCol;
    121.     delta_x=x2-x1; //计算坐标增量
    122.     delta_y=y2-y1;
    123.     uRow=x1;       //画线起点坐标
    124.     uCol=y1;
    125.     if(delta_x>0)incx=1; //设置单步方向
    126.     else if (delta_x==0)incx=0;//垂直线
    127.     else {incx=-1;delta_x=-delta_x;}
    128.     if(delta_y>0)incy=1;
    129.     else if (delta_y==0)incy=0;//水平线
    130.     else {incy=-1;delta_y=-delta_x;}
    131.     if(delta_x>delta_y)distance=delta_x; //选择基本增量坐标轴
    132.     else distance=delta_y;
    133.     for(t=0;t<distance+1;t++)
    134.     {
    135.         OLED_DrawPoint(uRow,uCol,mode);  //画点
    136.         xerr+=delta_x;
    137.         yerr+=delta_y;
    138.         if(xerr>distance)
    139.         {
    140.             xerr-=distance;
    141.             uRow+=incx;
    142.         }
    143.         if(yerr>distance)
    144.         {
    145.             yerr-=distance;
    146.             uCol+=incy;
    147.         }
    148.     }
    149. }
    150. //x,y:圆心坐标
    151. //r:圆的半径
    152. void OLED_DrawCircle(u8 x,u8 y,u8 r)
    153. {
    154.     int a, b,num;
    155.     a = 0;
    156.     b = r;
    157.     while(2 * b * b >= r * r)
    158.     {
    159.         OLED_DrawPoint(x + a, y - b,1);
    160.         OLED_DrawPoint(x - a, y - b,1);
    161.         OLED_DrawPoint(x - a, y + b,1);
    162.         OLED_DrawPoint(x + a, y + b,1);

    163.         OLED_DrawPoint(x + b, y + a,1);
    164.         OLED_DrawPoint(x + b, y - a,1);
    165.         OLED_DrawPoint(x - b, y - a,1);
    166.         OLED_DrawPoint(x - b, y + a,1);

    167.         a++;
    168.         num = (a * a + b * b) - r*r;   //计算画的点离圆心的距离
    169.         if(num > 0)
    170.         {
    171.             b--;
    172.             a--;
    173.         }
    174.     }
    175. }

    176. //在指定位置显示一个字符,包括部分字符
    177. //x:0~127
    178. //y:0~63
    179. //size1:选择字体 6x8/6x12/8x16/12x24
    180. //mode:0,反色显示;1,正常显示
    181. void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
    182. {
    183.     u8 i,m,temp,size2,chr1;
    184.     u8 x0=x,y0=y;
    185.     if(size1==8)size2=6;
    186.     else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占字节数
    187.     chr1=chr-' ';  //计算偏移后的值
    188.     for(i=0;i<size2;i++)
    189.     {
    190.         if(size1==8)
    191.         {temp=asc2_0806[chr1][i];} //调用0806字体
    192.         else if(size1==12)
    193.         {temp=asc2_1206[chr1][i];} //调用1206字体
    194.         else if(size1==16)
    195.         {temp=asc2_1608[chr1][i];} //调用1608字体
    196.         else if(size1==24)
    197.         {temp=asc2_2412[chr1][i];} //调用2412字体
    198.         else return;
    199.         for(m=0;m<8;m++)
    200.         {
    201.             if(temp&0x01)OLED_DrawPoint(x,y,mode);
    202.             else OLED_DrawPoint(x,y,!mode);
    203.             temp>>=1;
    204.             y++;
    205.         }
    206.         x++;
    207.         if((size1!=8)&&((x-x0)==size1/2))
    208.         {x=x0;y0=y0+8;}
    209.         y=y0;
    210.     }
    211. }

    212. //显示字符串
    213. //x,y:起点坐标
    214. //size1:字体大小
    215. //*chr:字符串起始地址
    216. //mode:0,反色显示;1,正常显示
    217. void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
    218. {
    219.     while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符
    220.     {
    221.         OLED_ShowChar(x,y,*chr,size1,mode);
    222.         if(size1==8)x+=6;
    223.         else x+=size1/2;
    224.         chr++;
    225.   }
    226. }

    227. //m^n
    228. u32 OLED_Pow(u8 m,u8 n)
    229. {
    230.     u32 result=1;
    231.     while(n--)
    232.     {
    233.       result*=m;
    234.     }
    235.     return result;
    236. }

    237. //显示数字
    238. //x,y :起点坐标
    239. //num :要显示的数字
    240. //len :数字的位数
    241. //size:字体大小
    242. //mode:0,反色显示;1,正常显示
    243. void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
    244. {
    245.     u8 t,temp,m=0;
    246.     if(size1==8)m=2;
    247.     for(t=0;t<len;t++)
    248.     {
    249.         temp=(num/OLED_Pow(10,len-t-1))%10;
    250.             if(temp==0)
    251.             {
    252.                 OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);
    253.             }
    254.             else
    255.             {
    256.               OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);
    257.             }
    258.     }
    259. }

    260. //显示汉字
    261. //x,y:起点坐标
    262. //num:汉字对应的序号
    263. //mode:0,反色显示;1,正常显示
    264. void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
    265. {
    266.     u8 m,temp;
    267.     u8 x0=x,y0=y;
    268.     u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占字节数
    269.     for(i=0;i<size3;i++)
    270.     {
    271.         if(size1==16)
    272.                 {temp=Hzk1[num][i];}//调用16*16字体
    273.         else if(size1==24)
    274.                 {temp=Hzk2[num][i];}//调用24*24字体
    275.         else if(size1==32)
    276.                 {temp=Hzk3[num][i];}//调用32*32字体
    277.         else if(size1==64)
    278.                 {temp=Hzk4[num][i];}//调用64*64字体
    279.         else return;
    280.         for(m=0;m<8;m++)
    281.         {
    282.             if(temp&0x01)OLED_DrawPoint(x,y,mode);
    283.             else OLED_DrawPoint(x,y,!mode);
    284.             temp>>=1;
    285.             y++;
    286.         }
    287.         x++;
    288.         if((x-x0)==size1)
    289.         {x=x0;y0=y0+8;}
    290.         y=y0;
    291.     }
    292. }

    293. //num 显示汉字的个数
    294. //space 每一遍显示的间隔
    295. //mode:0,反色显示;1,正常显示
    296. void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
    297. {
    298.     u8 i,n,t=0,m=0,r;
    299.     while(1)
    300.     {
    301.         if(m==0)
    302.         {
    303.         OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中
    304.             t++;
    305.         }
    306.         if(t==num)
    307.         {
    308.                 for(r=0;r<16*space;r++)     //显示间隔
    309.                  {
    310.                     for(i=1;i<144;i++)
    311.                         {
    312.                             for(n=0;n<8;n++)
    313.                             {
    314.                                 OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
    315.                             }
    316.                         }
    317.            OLED_Refresh();
    318.                  }
    319.         t=0;
    320.         }
    321.         m++;
    322.         if(m==16){m=0;}
    323.         for(i=1;i<144;i++)                  //实现左移
    324.         {
    325.             for(n=0;n<8;n++)
    326.             {
    327.                 OLED_GRAM[i-1][n]=OLED_GRAM[i][n];
    328.             }
    329.         }
    330.         OLED_Refresh();
    331.     }
    332. }

    333. //x,y:起点坐标
    334. //sizex,sizey,图片长宽
    335. //BMP[]:要写入的图片数组
    336. //mode:0,反色显示;1,正常显示
    337. void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
    338. {
    339.     u16 j=0;
    340.     u8 i,n,temp,m;
    341.     u8 x0=x,y0=y;
    342.     sizey=sizey/8+((sizey%8)?1:0);
    343.     for(n=0;n<sizey;n++)
    344.     {
    345.          for(i=0;i<sizex;i++)
    346.          {
    347.                 temp=BMP[j];
    348.                 j++;
    349.                 for(m=0;m<8;m++)
    350.                 {
    351.                     if(temp&0x01)OLED_DrawPoint(x,y,mode);
    352.                     else OLED_DrawPoint(x,y,!mode);
    353.                     temp>>=1;
    354.                     y++;
    355.                 }
    356.                 x++;
    357.                 if((x-x0)==sizex)
    358.                 {
    359.                     x=x0;
    360.                     y0=y0+8;
    361.                 }
    362.                 y=y0;
    363.      }
    364.      }
    365. }

    366. //OLED的初始化
    367. void OLED_Init(void)
    368. {
    369.     IIC_Init();
    370.     GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);

    371.     Delay_Ms(200);              //初始化之前的延时很重要

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

    复制代码
    oled.c文件主要进行OLED显示相关函数的配置。关于函数的具体功能请查看程序中函数具体注释,在此不再一一介绍。
    main.c文件
    1. /********************************** (C) COPYRIGHT *******************************
    2. * File Name          : main.c
    3. * Author             : WCH
    4. * Version            : V1.0.0
    5. * Date               : 2020/04/30
    6. * Description        : Main program body.
    7. *******************************************************************************/

    8. /*
    9. *@Note
    10. 模拟IIC驱动OLED屏:
    11. PA1连接OLED屏的SDA引脚
    12. PA2连接OLED屏的SCL引脚

    13. */

    14. #include "debug.h"
    15. #include "iic.h"
    16. #include "oled.h"
    17. #include "bmp.h"

    18. /*******************************************************************************
    19. * Function Name  : main
    20. * Description    : Main program.
    21. * Input          : None
    22. * Return         : None
    23. *******************************************************************************/
    24. int main(void)
    25. {
    26.     u8 t=' ';
    27.     Delay_Init();
    28.     OLED_Init();
    29.     OLED_ColorTurn(0);    //0正常显示,1 反色显示
    30.     OLED_DisplayTurn(0);  //0正常显示 1 屏幕翻转显示
    31.     while(1)
    32.     {
    33.         OLED_ShowPicture(0,0,128,64,BMP2,1);
    34.         OLED_Refresh();
    35.         Delay_Ms(500);
    36.         OLED_Clear();
    37.         OLED_ShowChinese(0,0,0,16,1); //沁
    38.         OLED_ShowChinese(18,0,1,16,1);//恒
    39.         OLED_ShowChinese(36,0,2,16,1);//单
    40.         OLED_ShowChinese(54,0,3,16,1);//片
    41.         OLED_ShowChinese(72,0,4,16,1);//机

    42.         OLED_ShowString(0,16,"IIC driving OLED",16,1);
    43.         OLED_ShowString(20,32,"2021/02/21",16,1);
    44.         OLED_ShowString(0,48,"ASCII:",16,1);
    45.         OLED_ShowString(63,48,"CODE:",16,1);
    46.         OLED_ShowChar(48,48,t,16,1);//显示ASCII字符
    47.         t++;
    48.         if(t>'~')t=' ';
    49.         OLED_ShowNum(103,48,t,3,16,1);
    50.         OLED_Refresh();
    51.         Delay_Ms(500);
    52.         OLED_Clear();
    53.         OLED_ShowString(0,0,"ABC",8,1);//6*8 “ABC”
    54.         OLED_ShowString(0,8,"ABC",12,1);//6*12 “ABC”
    55.         OLED_ShowString(0,20,"ABC",16,1);//8*16 “ABC”
    56.         OLED_ShowString(0,36,"ABC",24,1);//12*24 “ABC”
    57.         OLED_Refresh();
    58.         Delay_Ms(500);
    59.         OLED_ScrollDisplay(5,4,1);
    60.     }
    61. }

    复制代码
    main.c文件主要进行OLED屏显示操作,显示图片、汉字等信息。


    4、下载验证

    将编译好的程序下载到开发版并复位,OLED显示如下:
    CH32V CH573单片机芯片-第六十八章:CH32V103应用教程——IIC-模拟IIC驱动OLEDrisc-v单片机中文社区(1)


    67、模拟IIC-OLED.rar

    CH32V CH573单片机芯片-第六十八章:CH32V103应用教程——IIC-模拟IIC驱动OLEDrisc-v单片机中文社区(2) 67、模拟IIC-OLED.rar (498.48 KB, 下载次数: 16)
    链接:https://pan.baidu.com/s/1PrN3_9mp8LpEm23ySQBtJg
    提取码:6eha
    复制这段内容后打开百度网盘手机App,操作更方便哦







    上一篇:第六十七章:CH32V103应用教程——USART-DMA
    下一篇:第六十九章:CH32V103应用教程——IIC-硬件IIC驱动OLED
    RISCV作者优文
    全球首家只专注于RISC-V单片机行业应用的中文网站
    回复

    使用道具 举报

    高级模式
    B Color Image Link Quote Code Smilies

    本版积分规则

    关闭

    RISC-V单片机中文网上一条 /2 下一条



    版权及免责声明|RISC-V单片机中文网 |网站地图

    GMT+8, 2024-11-26 09:32 , Processed in 0.320752 second(s), 48 queries .

    快速回复 返回顶部 返回列表