sujingliang 发表于 2025-6-14 10:43

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

本帖最后由 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

//PB10:I2C1_SCL
//PB11:I2C1_SDA
void I2C_Configure(void)
{
   GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDefI2C_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    I2C_DeInit(I2C1);

    I2C_StructInit(&I2C_InitStruct);
    I2C_InitStruct.I2C_Mode       = I2C_MODE_MASTER;
    I2C_InitStruct.I2C_OwnAddress = I2C_OWN_ADDRESS;
    I2C_InitStruct.I2C_ClockSpeed = 400000;
    I2C_Init(I2C1, &I2C_InitStruct);

    I2C_TargetAddressConfig(I2C1, OLED_I2C_ADDRESS);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    I2C_Cmd(I2C1, ENABLE);
}


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

    for (i = 0; i < Length; i++)
    {
      I2C_SendData(I2C1, Buffer);

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

    I2C_TxData_Polling((uint8_t *)Buffer, Length);

    while (RESET == I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE))
    {
    }

    I2C_GenerateSTOP(I2C1);

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

4、OLED驱动函数
写命令
void OLED_WriteCommand(uint8_t cmd) {
      OLED_WriteFrame(0x00,&cmd,1);
}
写数据
// 发送数据
void OLED_WriteData(uint8_t data) {
    OLED_WriteFrame(0x40,&data,1);
}
OLED初始化
void OLED_Init(void) {
   
    PLATFORM_DelayMS(100);
    OLED_WriteCommand(0xAE); // 关闭显示
   
    OLED_WriteCommand(0xD5); // 设置显示时钟分频比/振荡器频率
    OLED_WriteCommand(0x80);
   
    OLED_WriteCommand(0xA8); // 设置多路复用率
    OLED_WriteCommand(0x3F);
   
    OLED_WriteCommand(0xD3); // 设置显示偏移
    OLED_WriteCommand(0x00);
   
    OLED_WriteCommand(0x40); // 设置显示开始行
   
    OLED_WriteCommand(0x8D); // 电荷泵设置
    OLED_WriteCommand(0x14);
   
    OLED_WriteCommand(0x20); // 设置内存地址模式
    OLED_WriteCommand(0x00); // 水平地址模式
   
    OLED_WriteCommand(0xA1); // 段重映射
    OLED_WriteCommand(0xC8); // 输出扫描方向
   
    OLED_WriteCommand(0xDA); // 设置COM引脚硬件配置
    OLED_WriteCommand(0x12);
   
    OLED_WriteCommand(0x81); // 设置对比度控制
    OLED_WriteCommand(0xCF);
   
    OLED_WriteCommand(0xD9); // 设置预充电周期
    OLED_WriteCommand(0xF1);
   
    OLED_WriteCommand(0xDB); // 设置VCOMH取消选择级别
    OLED_WriteCommand(0x40);
   
    OLED_WriteCommand(0xA4); // 设置整个显示开启/关闭
    OLED_WriteCommand(0xA6); // 设置正常/反向显示
   
    OLED_WriteCommand(0xAF); // 开启显示
   
    OLED_Clear();
    OLED_SetCursor(0, 0);
}

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

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

        while (*cn != '\0')
        {       
                if(*cn<128){
                        OLED_ShowChar(x,y,*cn,16,1);
                        cn+=1;        //下一个汉字偏移
                        y=y0;                //纵坐标恢复初始值
                        x0+=8;        //横坐标初始值加16
                        x=x0;                //横坐标=初始值
                }
                else
                {
                        for (i=0; i<(sizeof(Hzk1) / sizeof(Hzk1)); i++)
                        {
                                if((Hzk1.Index==*cn)&&(Hzk1.Index==*(cn+1)))
                                {
                                        num=i;
                                        break;
                                }
                        }

                        for(i=0;i<32;i++)
                        {
                                        temp=Hzk1.Msk;
                                        for(m=0;m<8;m++)
                                        {
                                                if(temp&0x01){if(inv) OLED_DrawPoint(x,y); else OLED_ClearPoint(x,y);}
                                                else {if(inv) OLED_ClearPoint(x,y); else OLED_DrawPoint(x,y);}
                                                temp>>=1;
                                                y++;
                                        }
                                        if(i==15){x=x0;}else {x++;}
                                        if(i>15){y=y0+8;}else {y=y0;}
                       }
                cn+=2;        //下一个汉字偏移
                y=y0;                //纵坐标恢复初始值
                x0+=16;        //横坐标初始值加16
                x=x0;                //横坐标=初始值
               }
        }
}指定2行(16像素高)内容循环左移
void OLED_ShiftTwoRowsLeft(uint8_t start_page, uint8_t shift_pixels) {

        uint8_t i,n;
        uint8_t temp;
        if(start_page > 6) return; // 最大从第6页开始(6+1=7<8)
        for (i=0;i<shift_pixels;i++)
        {
                temp=OLED_GRAM;
                temp=OLED_GRAM;
        }
        for(i=shift_pixels;i<VS_WIDTH;i++)   //实现左移
                {
                        for(n=start_page;n<start_page+2;n++)
                        {
                                OLED_GRAM=OLED_GRAM;
                        }
                }
        for(i=0;i<shift_pixels;i++)
        {
                OLED_GRAM=temp;
                OLED_GRAM=temp;
        }
}
5、测试一下

void I2C_Master_OLED_Sample(void)
{
        char buf;
        I2C_Configure();
        //配置RTC
RTC_Configure();
       
        OLED_Init();
        OLED_Clear_Buffer();
        OLED_ShowChineseString16(0, 0, (uint8_t*)"灵动微电子F0121开发板", 1);
        OLED_ShowChineseString16(0, 16, (uint8_t*)"Hello 21ic", 1);
        OLED_ShowChineseString16(0, 32, (uint8_t*)"Hello MM32F0121", 1);
        OLED_ShowChineseString16(0, 48, (uint8_t*)"I2C Master & OLED", 1);
        OLED_Refresh();
        while(1)
        {
                OLED_ShiftTwoRowsLeft(0, 3);
                sprintf(buf," -= %02d:%02d:%02d =- ",RTC_Calendar.hour,RTC_Calendar.minute,RTC_Calendar.second);
                OLED_ShowChineseString16(0, 48, (uint8_t*)buf,1);
                OLED_Refresh();

               
                PLATFORM_LED_Toggle(LED1);
                PLATFORM_LED_Toggle(LED2);

    PLATFORM_DelayMS(100);
        }
}

6、效果


页: [1]
查看完整版本: 【灵动微电子MM32F0121测评】5、 I2C主模式实验-驱动OLED