【灵动微电子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]