《Let'sdo第1期-DIY功率检测与控制系统》
目标:实现驱动OLED屏
1、连线图
2、I2C配置
static void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ }
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(hi2c->Instance==I2C1) { /* USER CODE BEGIN I2C1_MspInit 0 */ /* USER CODE END I2C1_MspInit 0 */ __HAL_RCC_GPIOB_CLK_ENABLE(); /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* Peripheral clock enable */ __HAL_RCC_I2C1_CLK_ENABLE(); /* I2C1 interrupt Init */ HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0); HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0); HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); /* USER CODE BEGIN I2C1_MspInit 1 */ /* USER CODE END I2C1_MspInit 1 */ } }
3、OLED驱动
// 发送命令
void OLED_WriteCommand(uint8_t cmd) { uint8_t buf[2] = {0x00, cmd}; HAL_I2C_Master_Transmit(hi2c_oled, OLED_I2C_ADDRESS, buf, 2, 10); }
// 发送数据
void OLED_WriteData(uint8_t data) { uint8_t buf[2] = {0x40, data}; HAL_I2C_Master_Transmit(hi2c_oled, OLED_I2C_ADDRESS, buf, 2, 10); }
和本次活动提供的代码的接口:
void OLED_WR_Byte(uint8_t dat,uint8_t mode) { if(mode==0){ OLED_WriteCommand(dat); } else { OLED_WriteData(dat); } }
// 初始化OLED
void OLED_Init(I2C_HandleTypeDef *hi2c) { hi2c_oled = hi2c; HAL_Delay(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); }
显示单个ascii字符
//inv:是否反显
void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t size1,uint8_t inv) { uint8_t i,m,temp,size2,chr1; uint8_t y0=y; size2=(size1/8+((size1%8)?1:0))*(size1/2); //得到字体一个字符对应点阵集所占的字节数 chr1=chr-' '; //计算偏移后的值 for(i=0;i<size2;i++) { if(size1==12) {temp=asc2_1206[chr1][i];} //调用1206字体 else if(size1==16) {temp=asc2_1608[chr1][i];} //调用1608字体 else if(size1==24) {temp=asc2_2412[chr1][i];} //调用2412字体 else return; for(m=0;m<8;m++) //写入数据 { if(temp&0x80){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((y-y0)==size1) { y=y0; x++; break; } } } }
显示ascii字符串
//inv:是否反显
void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t size1,uint8_t inv) { while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符! { OLED_ShowChar(x,y,*chr,size1,inv); x+=size1/2; if(x>128-size1) //换行 { x=0; y+=2; } chr++; } }
动画效果:屏幕整体左移:
void OLED_ScrollDisplay(uint8_t step) { uint8_t i,n; for(i=step;i<144;i++) //实现左移 { for(n=0;n<8;n++) { OLED_GRAM[i-step][n]=OLED_GRAM[i][n]; } } OLED_Refresh(); HAL_Delay(50); }
显示汉字字符串:
//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//取模方式 列行式
void OLED_ShowChineseString16(uint8_t x,uint8_t y,unsigned char *cn,uint8_t inv) { uint8_t i,m,n=0,temp,num; uint8_t x0=x,y0=y; while (*cn != '\0') { 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;//横坐标=初始值 } }
其中Hzk1修改为:
struct Cn16CharTypeDef // 汉字字模数据结构 { unsigned char Index[2]; // 汉字内码索引,一个汉字占两个字节 unsigned char Msk[32]; // 点阵码数据(32*29/8) }; const struct Cn16CharTypeDef Hzk1[]={ "功",{0x08,0x08,0x08,0xF8,0x08,0x08,0x08,0x10,0x10,0xFF,0x10,0x10,0x10,0xF0,0x00,0x00, 0x10,0x30,0x10,0x1F,0x08,0x88,0x48,0x30,0x0E,0x01,0x40,0x80,0x40,0x3F,0x00,0x00},/*"功",0*/ "率",{0x00,0x14,0xA4,0x44,0x24,0x34,0xAD,0x66,0x24,0x94,0x04,0x44,0xA4,0x14,0x00,0x00, 0x08,0x09,0x08,0x08,0x09,0x09,0x09,0xFD,0x09,0x09,0x0B,0x08,0x08,0x09,0x08,0x00},/*"率",1*/ "监",{0x00,0x00,0x7E,0x00,0x00,0xFF,0x00,0x40,0x30,0x0F,0x04,0x14,0x64,0x04,0x00,0x00, 0x40,0x40,0x7E,0x42,0x42,0x7E,0x42,0x42,0x42,0x7E,0x42,0x42,0x7E,0x40,0x40,0x00},/*"监",2*/ "测",{0x10,0x60,0x02,0x8C,0x00,0xFE,0x02,0xF2,0x02,0xFE,0x00,0xF8,0x00,0xFF,0x00,0x00, 0x04,0x04,0x7E,0x01,0x80,0x47,0x30,0x0F,0x10,0x27,0x00,0x47,0x80,0x7F,0x00,0x00},/*"测",3*/ "与",{0x00,0x00,0xE0,0x9F,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x08,0x00,0x00, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x48,0x80,0x40,0x3F,0x00,0x00,0x00},/*"与",4*/ "控",{0x10,0x10,0x10,0xFF,0x90,0x20,0x98,0x48,0x28,0x09,0x0E,0x28,0x48,0xA8,0x18,0x00, 0x02,0x42,0x81,0x7F,0x00,0x40,0x40,0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0x40,0x00},/*"控",5*/ "制",{0x40,0x50,0x4E,0x48,0x48,0xFF,0x48,0x48,0x48,0x40,0xF8,0x00,0x00,0xFF,0x00,0x00, 0x00,0x00,0x3E,0x02,0x02,0xFF,0x12,0x22,0x1E,0x00,0x0F,0x40,0x80,0x7F,0x00,0x00},/*"制",6*/ "电",{0x00,0x00,0xF8,0x88,0x88,0x88,0x88,0xFF,0x88,0x88,0x88,0x88,0xF8,0x00,0x00,0x00, 0x00,0x00,0x1F,0x08,0x08,0x08,0x08,0x7F,0x88,0x88,0x88,0x88,0x9F,0x80,0xF0,0x00},/*"电",7*/ "流",{0x10,0x60,0x02,0x8C,0x00,0x44,0x64,0x54,0x4D,0x46,0x44,0x54,0x64,0xC4,0x04,0x00, 0x04,0x04,0x7E,0x01,0x80,0x40,0x3E,0x00,0x00,0xFE,0x00,0x00,0x7E,0x80,0xE0,0x00},/*"流",8*/ "压",{0x00,0x00,0xFE,0x02,0x82,0x82,0x82,0x82,0xFA,0x82,0x82,0x82,0x82,0x82,0x02,0x00, 0x80,0x60,0x1F,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x44,0x58,0x40,0x40,0x00},/*"压",10*/ "阈",{0x00,0xF8,0x01,0x22,0xA0,0xA2,0xA2,0x22,0xFE,0x22,0xAA,0x32,0x02,0xFE,0x00,0x00, 0x00,0xFF,0x00,0x10,0x13,0x0A,0x4B,0x20,0x17,0x0C,0x13,0x38,0x80,0xFF,0x00,0x00},/*"阈",13*/ "值",{0x00,0x80,0x60,0xF8,0x07,0x04,0xE4,0xA4,0xA4,0xBF,0xA4,0xA4,0xE4,0x04,0x00,0x00, 0x01,0x00,0x00,0xFF,0x40,0x40,0x7F,0x4A,0x4A,0x4A,0x4A,0x4A,0x7F,0x40,0x40,0x00},/*"值",14*/ "过",{0x40,0x40,0x42,0xCC,0x00,0x08,0x48,0x88,0x08,0x08,0x08,0xFF,0x08,0x08,0x08,0x00, 0x00,0x40,0x20,0x1F,0x20,0x40,0x40,0x41,0x40,0x48,0x50,0x4F,0x40,0x40,0x40,0x00},/*"过",15*/ "状",{0x00,0x08,0x30,0x00,0xFF,0x20,0x20,0x20,0x20,0xFF,0x20,0x20,0x22,0x2C,0x20,0x00, 0x04,0x04,0x02,0x01,0xFF,0x80,0x40,0x30,0x0E,0x01,0x06,0x18,0x20,0x40,0x80,0x00},/*"状",17*/ "态",{0x00,0x04,0x84,0x84,0x44,0x24,0x54,0x8F,0x14,0x24,0x44,0x84,0x84,0x04,0x00,0x00, 0x41,0x39,0x00,0x00,0x3C,0x40,0x40,0x42,0x4C,0x40,0x40,0x70,0x04,0x09,0x31,0x00},/*"态",18*/ "正",{0x00,0x02,0x02,0xC2,0x02,0x02,0x02,0xFE,0x82,0x82,0x82,0x82,0x82,0x02,0x00,0x00, 0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00},/*"正",19*/ "常",{0x20,0x18,0x08,0xEA,0xAC,0xA8,0xA8,0xAF,0xA8,0xA8,0xAC,0xEA,0x08,0x28,0x18,0x00, 0x00,0x00,0x3E,0x02,0x02,0x02,0x02,0xFF,0x02,0x02,0x12,0x22,0x1E,0x00,0x00,0x00},/*"常",20*/ "异",{0x00,0x00,0x7E,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x92,0x9E,0x80,0xE0,0x00,0x00, 0x08,0x88,0x48,0x28,0x1E,0x08,0x08,0x08,0x08,0x08,0xFE,0x08,0x08,0x08,0x08,0x00},/*"异",21*/ "得",{0x00,0x10,0x88,0xC4,0x33,0x00,0xBE,0xAA,0xAA,0xAA,0xAA,0xAA,0xBE,0x80,0x00,0x00, 0x02,0x01,0x00,0xFF,0x00,0x02,0x0A,0x12,0x02,0x42,0x82,0x7F,0x02,0x02,0x02,0x00},/*"得",23*/ "捷",{0x10,0x10,0xFF,0x10,0x90,0x44,0x54,0x54,0x54,0xFF,0x54,0x54,0xF4,0x44,0x44,0x00, 0x42,0x82,0x7F,0x01,0x80,0x60,0x1D,0x21,0x41,0x7F,0x49,0x49,0x49,0x48,0x40,0x00},/*"捷",24*/ "子",{0x80,0x82,0x82,0x82,0x82,0x82,0x82,0xE2,0xA2,0x92,0x8A,0x86,0x82,0x80,0x80,0x00, 0x00,0x00,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"子",26*/ };
画图像:
//配置写入数据的起始位置 void OLED_WR_BP(uint8_t x,uint8_t y) { OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址 OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD); OLED_WR_Byte((x&0x0f),OLED_CMD); } //x0,y0:起点坐标 //x1,y1:终点坐标 //BMP[]:要写入的图片数组 void OLED_ShowPicture(uint8_t x0,uint8_t y0,uint8_t x1,uint8_t y1,uint8_t BMP[]) { uint32_t j=0; uint8_t x=0,y=0; if(y%8==0)y=0; else y+=1; for(y=y0/8;y<y1/8;y++) { OLED_WR_BP(x0,y); for(x=x0;x<x1;x++) { OLED_WR_Byte(BMP[j],OLED_DATA); j++; } } }
复制bmp到OLED_GRAM缓存:
void OLED_Copy_Bmp(uint8_t x0,uint8_t y0,uint8_t width,uint8_t height,const uint8_t BMP[],uint8_t bmp_x0,uint8_t bmp_y0) { uint8_t i,j; for (i=0;i<height/8;i++) for (j=0;j<width;j++) { OLED_GRAM[j+x0][i+y0/8]=BMP[i*128+j+bmp_x0+bmp_y0/8*128]; } }
main.c
OLED_Clear(); OLED_Copy_Bmp(0,0,128,16,bmp1,0,0); OLED_Refresh(); HAL_Delay(500); OLED_Copy_Bmp(0,16,128,16,bmp1,0,16); OLED_Refresh(); HAL_Delay(500); OLED_Copy_Bmp(0,32,128,16,bmp1,0,32); OLED_Refresh(); HAL_Delay(500); OLED_Copy_Bmp(0,48,128,16,bmp1,0,48); OLED_Refresh(); HAL_Delay(500); while (1) { if(count<128/4) { OLED_ScrollDisplay(4); count++; }else { OLED_ShowChineseString16(0,0,"电流电压阈值",(count)%4); OLED_ShowString(0,16,"I2C OLED Demo",16,(count+3)%4); OLED_ShowChineseString16(0,32,"得捷电子",(count+2)%4); OLED_ShowChineseString16(0,48,"功率监测与控制",(count+1)%4); OLED_Refresh(); HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin); HAL_Delay(1000); count++; } }