- //PB10:I2C1_SCL
- //PB11:I2C1_SDA
- void I2C_Configure(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- I2C_InitTypeDef I2C_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[i]);
- 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[VS_WIDTH][8];
所有关于绘图的函数都先在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[n][i],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[x][i]|=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[x][i]=~OLED_GRAM[x][i];
- OLED_GRAM[x][i]|=n;
- OLED_GRAM[x][i]=~OLED_GRAM[x][i];
- }
显示字符串
- 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[0])); i++)
- {
- if((Hzk1[i].Index[0]==*cn)&&(Hzk1[i].Index[1]==*(cn+1)))
- {
- num=i;
- break;
- }
- }
- for(i=0;i<32;i++)
- {
- temp=Hzk1[num].Msk[i];
- 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[VS_WIDTH][2];
- if(start_page > 6) return; // 最大从第6页开始(6+1=7<8)
- for (i=0;i<shift_pixels;i++)
- {
- temp[i][0]=OLED_GRAM[i][start_page];
- temp[i][1]=OLED_GRAM[i][start_page+1];
- }
- for(i=shift_pixels;i<VS_WIDTH;i++) //实现左移
- {
- for(n=start_page;n<start_page+2;n++)
- {
- OLED_GRAM[i-shift_pixels][n]=OLED_GRAM[i][n];
- }
- }
- for(i=0;i<shift_pixels;i++)
- {
- OLED_GRAM[VS_WIDTH-(shift_pixels-i)][start_page]=temp[i][0];
- OLED_GRAM[VS_WIDTH-(shift_pixels-i)][start_page+1]=temp[i][1];
- }
- }
5、测试一下
- void I2C_Master_OLED_Sample(void)
- {
- char buf[10];
- 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、效果