上一次帖子,已经通过网络获取天气数据,但是还没显示到显示屏上。今天的帖子我写的是关于如何取模天气图片,并显示天气图标和天气数据。
一、SSD1306显存填充显示板使用的OLED驱动芯片为SSD1306,我们来看看这款芯片的显存和该显存填充方式。下面是从SSD1303芯片手册上的截图,可以看得出它的显存是大小为128*64bit的RAM,并且分成为8页,PAGE0-PAGE7。显示板像素点大小为128*64,所以一个像素点对应RAM中的1bit。
往RAM的某一位写入1或者0,该位对应的像素点就显示亮点或者不显示。但是它是按照什么顺序写入数据的。下面也是从芯片手册上的截图。
可以看得出RAM填充顺序了 ,写入一个字节数据时,最低位数据写入一页中最上面那行,最高位写入一页中最下面那行。这样,使用取模软件取模图片或者字符时,我们才能知道按照什么顺序取模。之后再写相应代码,按照顺序把取模数据写入RAM中。
二、天气图标取模首先当然是先在网络下载我们需要的天气图标,大小看你像显示多大区域的图标,我的是48*48大小图标,在OLED上显示大小也是48*48。
我使用的是下面这款取模软件,我觉得图标取模不太好,不知道大家有没有推荐的。需要根据SSD1306进行取模软件的设置。比较重要的是取模方式的选择,这是跟SSD1306显示填充顺序对应的,我们需要选择列行式。其他数据点阵选择48,自定义格式选择C51。
选择晴图标,生成如下数组。
上面的小花就是太阳图标,是有点丑,先凑合用吧。
二、软件设计
1、显示天气图标
我们已经按照SSD1306显存填充顺序,取模好图片数据,接下来按照顺序把数据写入RAM就行了,代码如下。
- typedef enum
- {
- QINGLOGO =0,
- YINGLOGO,
- DUOYULOGO,
- YULOGO,
- LEIYULOGO,
- XUELOGO,
- }WEATHER_TYPE;
- void OLED_ShowWeather(uint8_t x,uint8_t y,WEATHER_TYPE type)
- {
- unsigned char (*temp)[48];
- //if(type ==qinglogo )
- //temp=Qing48;
- switch(type)
- {
- case QINGLOGO: temp=Qing48;break;
- case YINGLOGO: temp=Yin48;break;
- case YULOGO: temp=Yu48;break;
- case LEIYULOGO: temp=LeiYu48;break;
- case DUOYULOGO: temp=DuoYun48;break;
- case XUELOGO: temp=Xue48;break;
- default :
- temp=Qing48;
- break;
- }
- //OledSetPosition(x,y);
- for(uint8_t i=0;i<6;i++){
- OledSetPosition(x,y+i);
- for(uint8_t j = 0;j<48;j++){
- WriteData(temp[i][j]);
- }
- }
- }
复制代码 2、按键任务OLED显示板上的两个按键,主要用于界面切换、获取实时时间、天气数据。增加了按键任务,主要进行这两个按键的处理。右边按键,按下后可以切换界面,现在设置有四个界面。在时间显示界面按下左键,会获取实时时间。在天气显示界面,按下左键会获取实时天气。
- #include <stdio.h>
- #include <unistd.h>
- #include <stdbool.h>
- #include "ohos_init.h"
- #include "cmsis_os2.h"
- #include "wifiiot_gpio.h"
- #include "wifiiot_gpio_ex.h"
- #include "wifiiot_pwm.h"
- #include "wifiiot_adc.h"
-
- #include "oled_ssd1306.h"
-
- #define ANALOG_KEY_CHAN_NAME WIFI_IOT_ADC_CHANNEL_2
-
- typedef enum{
- TIMESCREEN=0,
- NOWSCREEN,
- TOSCREEN,
- ATOSCREEN,
- } SCREEN_STATUS;
- extern SCREEN_STATUS Now_Screen ;
- extern SCREEN_STATUS Last_Screen ;
-
- typedef enum{
- GET_NORMAL =0 ,
- GET_PROPRESS,
- GET_SUC,
- GET_FAIL,
- }GET_STATUS;
-
- GET_STATUS Get_Status = GET_NORMAL;
- GET_STATUS Last_Get_Status = GET_NORMAL;
-
- extern void getNtpTime(void);
- extern bool getWeather(void);
-
- static float ConvertToVoltage(unsigned short data)
- {
- return (float)data * 1.8 * 4 / 4096; /* adc code equals: voltage/4/1.8*4096 */
- }
-
- static void KeyTask(void *arg)
- {
- (void)arg;
- unsigned short data = 0;
-
- GpioInit();
- static bool keyflag = false;
- while(1)
- {
- AdcRead(ANALOG_KEY_CHAN_NAME, &data, WIFI_IOT_ADC_EQU_MODEL_4, WIFI_IOT_ADC_CUR_BAIS_DEFAULT, 0);
- float voltage = ConvertToVoltage(data);
-
- if((voltage>0.45 && voltage<0.65)&&(!keyflag))
- {
- keyflag = true;
- //OledShowString(16,7,"Sync time...",1);
- //getNtpTime();
- //OledFillScreen(0);
- switch (Now_Screen){
- case TIMESCREEN:
- //OledShowString(16,7,"Sync time...",1);
- Get_Status = GET_PROPRESS;
- getNtpTime();
- //OledFillScreen(0);
- Get_Status = GET_SUC;
- break;
- case NOWSCREEN:
- //OledShowString(0,7,"Get Weather...",1);
- Get_Status = GET_PROPRESS;
- if(getWeather())
- //OledFillScreen(0);
- Get_Status = GET_SUC;
- else
- {
- //OledShowString(0,7,"Get fail...",1);
- Get_Status = GET_FAIL;
- }
- break;
- case TOSCREEN:
- Get_Status = GET_PROPRESS;
- if(getWeather())
- Get_Status = GET_SUC;
- else
- {
- Get_Status = GET_FAIL;
- }
- break;
- case ATOSCREEN:
- Get_Status = GET_PROPRESS;
- if(getWeather())
- Get_Status = GET_SUC;
- else
- {
- Get_Status = GET_FAIL;
- }
- break;
-
- default:
- break;
- }
- }
- else if((voltage>0.9 && voltage<1)&&(!keyflag))
- {
- keyflag = true;
- Now_Screen ++;
- if(Now_Screen > ATOSCREEN)
- {
- Now_Screen = TIMESCREEN;
- }
- }
-
- if((!(voltage>0.45 && voltage<0.65)) && (!(voltage>0.9 && voltage<1))) {
- keyflag = false;
- }
-
- usleep(300000);
- }
- }
-
- static void KeyTaskHandle(void)
- {
- osThreadAttr_t attr;
- attr.name = "KeyTask";
- attr.attr_bits = 0U;
- attr.cb_mem = NULL;
- attr.cb_size = 0U;
- attr.stack_mem = NULL;
- attr.stack_size = 4096;
- attr.priority = osPriorityNormal+2;
-
- if (osThreadNew(KeyTask, NULL, &attr) == NULL) {
- printf("[KeyTaskHandle] Falied to create KeyTask!n");
- }
- }
- APP_FEATURE_INIT(KeyTaskHandle);
复制代码 3、 OLED任务修改修改oled_demo.c内容。增加页面切换,获取时间、天气数据情况提示,显示天气数据和天气图标。
- typedef enum{
- TIMESCREEN=0,
- NOWSCREEN,
- TOSCREEN,
- ATOSCREEN,
- } SCREEN_STATUS;
- SCREEN_STATUS Now_Screen = TIMESCREEN;
- SCREEN_STATUS Last_Screen = TIMESCREEN;
-
- typedef enum{
- GET_NORMAL =0 ,
- GET_PROPRESS,
- GET_SUC,
- GET_FAIL,
- }GET_STATUS;
- extern GET_STATUS Get_Status ;
- extern GET_STATUS Last_Get_Status ;
-
- static uint8_t i=0;
-
- static void TimeScreenDisp(void)
- {
- //OledShow
- rtc_time_t mData;
- OLED_ShowCHinese(0,3,0);//温
- OLED_ShowCHinese(16,3,2);//度
- OledShowChar(32,3,':',2);
- sprintf((char *)TimeStr,"%02d",
- (uint32_t)temperature);
- OledShowString(40,3,(char *)TimeStr,2);
- OLED_ShowCHinese(64,3,1);//湿
- OLED_ShowCHinese(80,3,2);//度
- OledShowChar(96,3,':',2);
- sprintf((char *)TimeStr,"%02d",
- (uint32_t)humidity);
- OledShowString(104,3,(char *)TimeStr,2);
- covUnixTimeStp2Beijing(timedata, &mData);
- sprintf((char *)TimeStr,"%04d-%02d-%02d",
- mData.ui8Year, mData.ui8Month, mData.ui8DayOfMonth);
- OledShowString(24,0,(char *)TimeStr,1);
- sprintf((char *)TimeStr,"%02d:%02d:%02d",
- mData.ui8Hour,mData.ui8Minute,mData.ui8Second);
- OledShowString(32,1,(char *)TimeStr,1);
-
- if(Get_Status != Last_Get_Status)
- {
- Last_Get_Status = Get_Status;
- switch ( Get_Status)
- {
- case GET_NORMAL:
- break;
- case GET_PROPRESS:
- OledShowString(16,7,"Sync time...",1);
- break;
- case GET_SUC:
- OledClearString(16,7,"Sync time...",1);
- OledShowString(16,7,"Sync Suc...",1);
- break;
- case GET_FAIL:
- OledClearString(16,7,"Sync time...",1);
- OledShowString(16,7,"Sync fail...",1);
- break;
- default:
- break;
- }
- }
- }
-
- static void DispWeather(uint8_t x, uint8_t y ,uint8_t code){
- switch(code)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- OLED_ShowWeather(x,y,QINGLOGO);
- break;
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- OLED_ShowWeather(x,y,DUOYULOGO);
- break;
- case 9:
-
- case 10:
- OLED_ShowWeather(x,y,YINGLOGO);
- break;
- case 11:
- case 12:
-
- OLED_ShowWeather(x,y,LEIYULOGO);
- break;
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- case 18:
- case 19:
- OLED_ShowWeather(x,y,YULOGO);
- break;
- case 20:
- case 21:
- case 22:
- case 23:
- case 24:
- case 25:
- OLED_ShowWeather(x,y,XUELOGO);
- break;
- case 30:
- //OLED_ShowWeather(x,y,YULOGO);
- //break;
- default:
- OLED_ShowWeather(x,y,QINGLOGO);
- break;
- }
-
- }
-
- extern weather weatherValue;
- static void WeatherDisp(SCREEN_STATUS Screen){
- switch (Screen)
- {
- case NOWSCREEN:
- DispWeather(5, 1 ,weatherValue.nowcode);
- OLED_ShowCHinese(56,0,9);//今
- OLED_ShowCHinese(72,0,12);//天
- sprintf((char *)DispStr,"%02d",
- (uint32_t)weatherValue.nowtemp);
- OledShowString(60,5,(char *)DispStr,2);
- OLED_ShowCHinese(76,5,13);//天
- sprintf((char *)DispStr,"%02d/%02d",
- (uint32_t)weatherValue.low[Screen-1],(uint32_t)weatherValue.high[Screen-1]);
- OledShowString(60,3,(char *)DispStr,2);
- sprintf((char *)DispStr,"%d%%",
- (uint32_t)weatherValue.humi[Screen-1]);
- OledShowString(100,5,(char *)DispStr,2);
- break;
- case TOSCREEN:
- DispWeather(5, 1 ,weatherValue.code[Screen-1]);
- OLED_ShowCHinese(56,0,10);//明
- OLED_ShowCHinese(72,0,12);//天
- sprintf((char *)DispStr,"%02d/%02d",
- (uint32_t)weatherValue.low[Screen-1],(uint32_t)weatherValue.high[Screen-1]);
- OledShowString(60,3,(char *)DispStr,2);
- sprintf((char *)DispStr,"%d%%",
- (uint32_t)weatherValue.humi[Screen-1]);
- OledShowString(70,5,(char *)DispStr,2);
- break;
- case ATOSCREEN:
- DispWeather(5, 1 ,weatherValue.code[Screen-1]);
- OLED_ShowCHinese(56,0,11);//后
- OLED_ShowCHinese(72,0,12);//天
- sprintf((char *)DispStr,"%02d/%02d",
- (uint32_t)weatherValue.low[Screen-1],(uint32_t)weatherValue.high[Screen-1]);
- OledShowString(60,3,(char *)DispStr,2);
- sprintf((char *)DispStr,"%d%%",
- (uint32_t)weatherValue.humi[Screen-1]);
- OledShowString(70,5,(char *)DispStr,2);
- break;
- default:
- break;
- }
- if(Get_Status != Last_Get_Status)
- {
- Last_Get_Status = Get_Status;
- switch ( Get_Status)
- {
- case GET_NORMAL:
- break;
- case GET_PROPRESS:
- OledShowString(0,7,"Get Weather...",1);
- break;
- case GET_SUC:
- OledClearString(0,7,"Get Weather...",1);
- OledShowString(0,7,"Get Suc...",1);
- i=0;
- break;
- case GET_FAIL:
- OledClearString(0,7,"Get Weather...",1);
- OledShowString(0,7,"Get fail...",1);
- i=0;
- break;
- default:
- break;
- }
- }
- }
- static void OledTask(void *arg)
- {
- (void)arg;
- GpioInit();
- OledInit();
- OledFillScreen(0x00);
-
- while (1) {
- if(Now_Screen != Last_Screen)
- {
- Last_Screen = Now_Screen;
- OledFillScreen(0);
- Get_Status = GET_NORMAL;
- Last_Get_Status = GET_NORMAL;
- i=0;
- }
-
- if((Get_Status == GET_FAIL) ||(Get_Status == GET_SUC))
- {
- i++;
- if(i > 20)
- {
- Get_Status = GET_NORMAL;
- i=0;
-
- switch(Now_Screen){
- case TIMESCREEN:
- OledClearString(16,7,"Sync time...",1);
- break;
- case NOWSCREEN:
-
- case TOSCREEN:
-
- case ATOSCREEN:
- printf("clear oled i = %dn",i);
- OledClearString(0,7,"Get Weather...",1);
- break;
- default :
- break;
-
- }
- }
- }
-
- switch (Now_Screen){
-
- case TIMESCREEN:
- TimeScreenDisp();
- break;
-
- case NOWSCREEN:
- WeatherDisp(NOWSCREEN);
- break;
-
- case TOSCREEN:
- WeatherDisp(TOSCREEN);
- break;
-
- case ATOSCREEN:
- WeatherDisp(ATOSCREEN);
- break;
-
- default:
- break;
- }
- usleep(100000);
- }
- }
复制代码 4、加入rtc.c
现在时间调整是通过每一秒时间戳加1,再把时间戳转化为北京时间,到达时间的更新。为了减少任务中因为其他函数存在,增加时间误差,所以单独创建一个任务来进行时间戳的调整。- #include <stdio.h>
- #include <unistd.h>
- #include <stdbool.h>
-
- #include "ohos_init.h"
- #include "cmsis_os2.h"
- uint32_t timedata=1608362368;
-
- static void RtcTask(void *arg)
- {
- (void)arg;
- while(1)
- {
- timedata++;
- sleep(1);
- }
- }
-
- static void RtcTaskHandle(void)
- {
- osThreadAttr_t attr;
- attr.name = "RtcTask";
- attr.attr_bits = 0U;
- attr.cb_mem = NULL;
- attr.cb_size = 0U;
- attr.stack_mem = NULL;
- attr.stack_size = 512;
- attr.priority = osPriorityNormal;
-
- if (osThreadNew(RtcTask, NULL, &attr) == NULL) {
- printf("[RtcTaskHandle] Falied to create KeyTask!n");
- }
- }
- APP_FEATURE_INIT(RtcTaskHandle);
复制代码
5、修改BUILD.gn
修改OLED文件夹下的BUILD.gn文件,sources中加入keytask.c
- sources = [
- "oled_demo.c",
- "oled_ssd1306.c",
- "timeconv.c",
- "envrionment_demo.c",
- "aht20.c",
- "wifi_connecter.c",
- "getNTP.c",
- "getweather.c",
- "cjsonparse.c",
- "keytask.c",
- "rtc.c"
- ]
复制代码 三、结果演示按下右边按键,可以切换界面,有四个界面,分别为时间显示界面,今天、明天、后天天气显示界面。在时间显示界面按下左键,会获取实时时间,在天气显示界面,按下左键会获取实时天气,并且显示天气获取情况提示。
四、总结这一篇先写到这里,下一篇是关于通过一个公网的中转服务器,进行wifiiot和手机之间的远程数据传输。