[MM32硬件] 【灵动微电子MM32F0121测评】5、 I2C主模式实验-驱动OLED

[复制链接]
 楼主| sujingliang 发表于 2025-6-14 10:43 | 显示全部楼层 |阅读模式
本帖最后由 sujingliang 于 2025-6-14 10:43 编辑

本文目的:采用硬件I2C主模式方式驱动SSD1306 OLED显示屏

关于I2C例程可参见:
LibSamples_MM32F0120_V1.13.4\Samples\LibSamples\I2C\I2C_Master_EEPROM_Polling

本文的I2C初始化,I2C写数据帧的函数源于上面例程

1、I2C初始化
I2C时钟设置为400khz:I2C_ClockSpeed=400000

I2C目标地址:I2C_TargetAddressConfig(I2C1, OLED_I2C_ADDRESS);
#define OLED_I2C_ADDRESS 0x78  // OLED 8位I2C地址

PB10:I2C1_SCL;PB11:I2C1_SDA

  1. //PB10:I2C1_SCL
  2. //PB11:I2C1_SDA
  3. void I2C_Configure(void)
  4. {
  5.      GPIO_InitTypeDef GPIO_InitStruct;
  6.     I2C_InitTypeDef  I2C_InitStruct;

  7.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

  8.     I2C_DeInit(I2C1);

  9.     I2C_StructInit(&I2C_InitStruct);
  10.     I2C_InitStruct.I2C_Mode       = I2C_MODE_MASTER;
  11.     I2C_InitStruct.I2C_OwnAddress = I2C_OWN_ADDRESS;
  12.     I2C_InitStruct.I2C_ClockSpeed = 400000;
  13.     I2C_Init(I2C1, &I2C_InitStruct);

  14.     I2C_TargetAddressConfig(I2C1, OLED_I2C_ADDRESS);

  15.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

  16.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
  17.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);

  18.     GPIO_StructInit(&GPIO_InitStruct);
  19.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
  20.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  21.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_OD;
  22.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  23.     I2C_Cmd(I2C1, ENABLE);
  24. }



2、I2C Polling方式写数据
发送数据有需要等待完成
  1. void I2C_TxData_Polling(uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint8_t i = 0;

  4.     for (i = 0; i < Length; i++)
  5.     {
  6.         I2C_SendData(I2C1, Buffer[i]);

  7.         while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  8.         {
  9.         }
  10.     }
  11. }
3、写I2C数据帧
直接借鉴例程中的EEPROM_WritePage函数,改了个名字
  1. void OLED_WriteFrame(uint8_t Address, uint8_t *Buffer, uint8_t Length)
  2. {
  3.         
  4.     I2C_TxData_Polling((uint8_t *)&Address, 0x01);

  5.     I2C_TxData_Polling((uint8_t *)Buffer, Length);

  6.     while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  7.     {
  8.     }

  9.     I2C_GenerateSTOP(I2C1);

  10.     while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
  11.     {
  12.     }
  13. }
有了OLED_WriteFrame函数就可以写OLED的驱动函数

4、OLED驱动函数
写命令
  1. void OLED_WriteCommand(uint8_t cmd) {
  2.         OLED_WriteFrame(0x00,&cmd,1);
  3. }
写数据
  1. // 发送数据
  2. void OLED_WriteData(uint8_t data) {
  3.     OLED_WriteFrame(0x40,&data,1);
  4. }
OLED初始化
  1. void OLED_Init(void) {
  2.    
  3.     PLATFORM_DelayMS(100);
  4.     OLED_WriteCommand(0xAE); // 关闭显示
  5.    
  6.     OLED_WriteCommand(0xD5); // 设置显示时钟分频比/振荡器频率
  7.     OLED_WriteCommand(0x80);
  8.    
  9.     OLED_WriteCommand(0xA8); // 设置多路复用率
  10.     OLED_WriteCommand(0x3F);
  11.    
  12.     OLED_WriteCommand(0xD3); // 设置显示偏移
  13.     OLED_WriteCommand(0x00);
  14.    
  15.     OLED_WriteCommand(0x40); // 设置显示开始行
  16.    
  17.     OLED_WriteCommand(0x8D); // 电荷泵设置
  18.     OLED_WriteCommand(0x14);
  19.    
  20.     OLED_WriteCommand(0x20); // 设置内存地址模式
  21.     OLED_WriteCommand(0x00); // 水平地址模式
  22.    
  23.     OLED_WriteCommand(0xA1); // 段重映射
  24.     OLED_WriteCommand(0xC8); // 输出扫描方向
  25.    
  26.     OLED_WriteCommand(0xDA); // 设置COM引脚硬件配置
  27.     OLED_WriteCommand(0x12);
  28.    
  29.     OLED_WriteCommand(0x81); // 设置对比度控制
  30.     OLED_WriteCommand(0xCF);
  31.    
  32.     OLED_WriteCommand(0xD9); // 设置预充电周期
  33.     OLED_WriteCommand(0xF1);
  34.    
  35.     OLED_WriteCommand(0xDB); // 设置VCOMH取消选择级别
  36.     OLED_WriteCommand(0x40);
  37.    
  38.     OLED_WriteCommand(0xA4); // 设置整个显示开启/关闭
  39.     OLED_WriteCommand(0xA6); // 设置正常/反向显示
  40.    
  41.     OLED_WriteCommand(0xAF); // 开启显示
  42.    
  43.     OLED_Clear();
  44.     OLED_SetCursor(0, 0);
  45. }


定义一个虚拟屏幕缓存
#define VS_WIDTH 170
uint8_t OLED_GRAM[VS_WIDTH][8];

所有关于绘图的函数都先在OLED_GRAM中完成,再通过OLED_Refresh()函数更新到OLED显示
  1. //用于刷新OLED显示内容       
  2. void OLED_Refresh(void)
  3. {
  4.         uint8_t i,n;
  5.         for(i=0;i<8;i++)
  6.         {
  7.            OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
  8.            OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
  9.            OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
  10.            for(n=0;n<128;n++)
  11.                  OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
  12.   }
  13. }
画点函数
  1. void OLED_DrawPoint(uint8_t x,uint8_t y)
  2. {
  3.         uint8_t i,m,n;
  4.         if(x>=VS_WIDTH||y>=64) return;
  5.         i=y/8;
  6.         m=y%8;
  7.         n=1<<m;
  8.         OLED_GRAM[x][i]|=n;
  9. }
清除一个点
  1. void OLED_ClearPoint(uint8_t x,uint8_t y)
  2. {
  3.         uint8_t i,m,n;
  4.         if(x>=VS_WIDTH||y>=64) return;
  5.         i=y/8;
  6.         m=y%8;
  7.         n=1<<m;
  8.         OLED_GRAM[x][i]=~OLED_GRAM[x][i];
  9.         OLED_GRAM[x][i]|=n;
  10.         OLED_GRAM[x][i]=~OLED_GRAM[x][i];
  11. }
显示字符串
  1. void OLED_ShowChineseString16(uint8_t x,uint8_t y,unsigned char *cn,uint8_t inv)
  2. {
  3.         uint8_t i,m;
  4.         uint8_t temp,num;
  5.         uint8_t x0=x,y0=y;
  6.        

  7.         while (*cn != '\0')
  8.         {       
  9.                 if(*cn<128){
  10.                         OLED_ShowChar(x,y,*cn,16,1);
  11.                         cn+=1;        //下一个汉字偏移
  12.                         y=y0;                //纵坐标恢复初始值
  13.                         x0+=8;        //横坐标初始值加16
  14.                         x=x0;                //横坐标=初始值
  15.                 }
  16.                 else
  17.                 {
  18.                         for (i=0; i<(sizeof(Hzk1) / sizeof(Hzk1[0])); i++)
  19.                         {
  20.                                 if((Hzk1[i].Index[0]==*cn)&&(Hzk1[i].Index[1]==*(cn+1)))
  21.                                 {
  22.                                         num=i;
  23.                                         break;
  24.                                 }
  25.                         }

  26.                         for(i=0;i<32;i++)
  27.                         {
  28.                                         temp=Hzk1[num].Msk[i];
  29.                                         for(m=0;m<8;m++)
  30.                                         {
  31.                                                 if(temp&0x01){if(inv) OLED_DrawPoint(x,y); else OLED_ClearPoint(x,y);}
  32.                                                 else {if(inv) OLED_ClearPoint(x,y); else OLED_DrawPoint(x,y);}
  33.                                                 temp>>=1;
  34.                                                 y++;
  35.                                         }
  36.                                         if(i==15){x=x0;}else {x++;}
  37.                                         if(i>15){y=y0+8;}else {y=y0;}
  38.                          }
  39.                 cn+=2;        //下一个汉字偏移
  40.                 y=y0;                //纵坐标恢复初始值
  41.                 x0+=16;        //横坐标初始值加16
  42.                 x=x0;                //横坐标=初始值
  43.                  }
  44.         }
  45. }
指定2行(16像素高)内容循环左移
  1. void OLED_ShiftTwoRowsLeft(uint8_t start_page, uint8_t shift_pixels) {
  2.   
  3.         uint8_t i,n;
  4.         uint8_t temp[VS_WIDTH][2];
  5.         if(start_page > 6) return; // 最大从第6页开始(6+1=7<8)
  6.         for (i=0;i<shift_pixels;i++)
  7.         {
  8.                 temp[i][0]=OLED_GRAM[i][start_page];
  9.                 temp[i][1]=OLED_GRAM[i][start_page+1];
  10.         }
  11.         for(i=shift_pixels;i<VS_WIDTH;i++)   //实现左移
  12.                 {
  13.                         for(n=start_page;n<start_page+2;n++)
  14.                         {
  15.                                 OLED_GRAM[i-shift_pixels][n]=OLED_GRAM[i][n];
  16.                         }
  17.                 }
  18.         for(i=0;i<shift_pixels;i++)
  19.         {
  20.                 OLED_GRAM[VS_WIDTH-(shift_pixels-i)][start_page]=temp[i][0];
  21.                 OLED_GRAM[VS_WIDTH-(shift_pixels-i)][start_page+1]=temp[i][1];
  22.         }
  23. }

5、测试一下

  1. void I2C_Master_OLED_Sample(void)
  2. {
  3.         char buf[10];
  4.         I2C_Configure();
  5.         //配置RTC
  6.   RTC_Configure();
  7.        
  8.         OLED_Init();
  9.         OLED_Clear_Buffer();
  10.         OLED_ShowChineseString16(0, 0, (uint8_t*)"灵动微电子F0121开发板", 1);
  11.         OLED_ShowChineseString16(0, 16, (uint8_t*)"Hello 21ic", 1);
  12.         OLED_ShowChineseString16(0, 32, (uint8_t*)"Hello MM32F0121", 1);
  13.         OLED_ShowChineseString16(0, 48, (uint8_t*)"I2C Master & OLED", 1);
  14.         OLED_Refresh();
  15.         while(1)
  16.         {
  17.                 OLED_ShiftTwoRowsLeft(0, 3);
  18.                 sprintf(buf," -= %02d:%02d:%02d =- ",RTC_Calendar.hour,RTC_Calendar.minute,RTC_Calendar.second);
  19.                 OLED_ShowChineseString16(0, 48, (uint8_t*)buf,1);
  20.                 OLED_Refresh();

  21.                
  22.                 PLATFORM_LED_Toggle(LED1);
  23.                 PLATFORM_LED_Toggle(LED2);

  24.     PLATFORM_DelayMS(100);
  25.         }
  26. }

6、效果
tutieshi_640x357_5s (1).gif

您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

146

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部

84

主题

146

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部