设计背景
我组大部分成员皆是初次接触嵌入式开发系统,因此根据本次夏令营学习及导师推荐下我们选择了设计难度较低的oled时钟设计。在导师的帮助下完成了整个工程的搭建。
设计思路
采用市面上比较常见的ssd1306显示屏作为时钟的载体。设计的思路是:
1.使用本次夏令营下发的psoc6-evaluationkit-062s2开发板驱动oled屏
2.使用rt-thread studio设置rtc获取时间
3.将时间显示在oled屏上并设计简单的数字滚动功能
开发工具
1.psoc6-evaluationkit-062s2开发板
2.rt-thread studio
3.ssd1306四排真oled屏
rt-thread studio软件包设置
其中参考了ssd1306软件包及rtc例程因此ssd1306软件包需单独下载
开发心得
作为初次接触嵌入式操作系统的初学者,学习中有些许困难。主要在于不能清楚的掌握模块的调用,初次接触线程的概念、使用经过rtt封装过的c语言头文件及rtt中的基本函数。在大致读懂后明白了rtt已经将许多模块写好可以直接调用,各位前辈的软件包是的开发更具模块化。裸机开发与嵌入式操作系统开发的区别很大但eos的简洁明了显而易见,不过代码的移植也是入门的难点之一。
下附完整的工程代码
工程代码
1.oled屏驱动
#include oled.h
#include
#include
#include codetab.h
//oled显示尺寸
uint16_t const displaywidth = 128;
uint16_t const displayheight = 64;
/* oled显存
[0]0 1 2 3 ... 127
[1]0 1 2 3 ... 127
[2]0 1 2 3 ... 127
[3]0 1 2 3 ... 127
[4]0 1 2 3 ... 127
[5]0 1 2 3 ... 127
[6]0 1 2 3 ... 127
[7]0 1 2 3 ... 127 /
static uint8_t oled_ram[8][128];//定义gddram缓存区
#define cw_oled_i2c cw_i2c1
#define oled_addr 0x3c
#define ssd1306_ctrl_cmd 0x00
#define ssd1306_ctrl_data 0x40
#define ssd1306_mask_cont (0x1<<7)
static struct rt_i2c_bus_device i2c_bus;
void oled_i2c_init(void)
{
i2c_bus = (struct rt_i2c_bus_device )rt_device_find(pkg_using_ssd1306_i2c_bus_name);
if (i2c_bus == rt_null)
{
rt_kprintf(can not find %s device, pkg_using_ssd1306_i2c_bus_name);
return;
}
}
//向oled寄存器地址写一个byte的数据
//int i2c_writebyte(uint8_t addr,uint8_t data)
//{
//
// return 0;
//}
/ ***********************************************************
prototype : void writecmd(uint8_t iic_command)
parameters : iic_command
return : none
description : 写命令
/
void writecmd(uint8_t iic_command)
{
// i2c_writebyte(0x00, iic_command);
uint8_t buf[2] = {ssd1306_ctrl_cmd, iic_command};
rt_i2c_master_send(i2c_bus, oled_addr, rt_i2c_wr, buf, 2);
}
/
prototype : void writedat(uint8_t iic_data)
parameters : iic_data
return : none
description : 写数据
/
void writedat(uint8_t iic_data)
{
//i2c_writebyte(0x40, iic_data);
uint8_t buf[2] = {ssd1306_ctrl_data, iic_data};
rt_i2c_master_send(i2c_bus, oled_addr, rt_i2c_wr, buf, 2);
}
/
prototype : void oled_init(void)
parameters : none
return : none
description : 初始化oled模块
/
void oled_init(void)
{
oled_i2c_init();
rt_thread_mdelay(500);
writecmd(0xae); //开显示
writecmd(0x20); //设置内存寻址模式
writecmd(0x00); //00,水平寻址模式;01,垂直寻址模式;10,页面寻址模式(重置);11,无效
writecmd(0x21); //设置列开始和结束地址
writecmd(0x00); //列起始地址,范围:0 – 127d (默认值 = 0d)
writecmd(0x7f); //列结束地址,范围:0 – 127d (默认值 = 127d)
writecmd(0x22); //页面设置开始和结束地址
writecmd(0x00); //页面起始地址,范围:0-7d (默认值= 0d)
writecmd(0x07); //页面结束地址,范围:0-7d (默认值= 7d)
writecmd(0xc8); //设置com输出扫描方向
writecmd(0x40); //--设置起始行地址
writecmd(0x81); //--set contrast control register
writecmd(0xff); //亮度调节 0x00~0xff
writecmd(0xa1); //--设置段重新映射0到127
writecmd(0xa6); //--设置正常显示
writecmd(0xa8); //--设置复用比(1 ~ 64)
writecmd(0x3f); //
writecmd(0xa4); //0xa4,输出遵循ram内容;0xa5,output忽略ram内容
writecmd(0xd3); //-设置显示抵消
writecmd(0x00); //-not offset
writecmd(0xd5); //--设置显示时钟分频/振荡器频率
writecmd(0xf0); //--设置分率
writecmd(0xd9); //--设置pre-charge时期
writecmd(0x22); //
writecmd(0xda); //--设置com大头针硬件配置
writecmd(0x12);
writecmd(0xdb); //--设置vcomh
writecmd(0x20); //0x20,0.77xvcc
writecmd(0x8d); //--设置dc-dc
writecmd(0x14); //
writecmd(0xaf); //--打开oled面板
oled_fullyclear();//清屏
}
/
prototype : void oled_on(void)
parameters : none
return : none
description : 将oled从休眠中唤醒
/
void oled_on(void)
{
writecmd(0x8d); //设置电荷泵
writecmd(0x14); //开启电荷泵
writecmd(0xaf); //oled唤醒
}
/
prototype : void oled_off(void)
parameters : none
return : none
description : 让oled休眠 -- 休眠模式下,oled功耗不到10ua
/
void oled_off(void)
{
writecmd(0x8d); //设置电荷泵
writecmd(0x10); //关闭电荷泵
writecmd(0xae); //oled休眠
}
/
prototype : void oled_refreshram(void)
parameters : none
return : none
description : 全屏填充
/
void oled_refreshram(void)
{
for(uint16_t m = 0; m < displayheight/8; m++)
{
for(uint16_t n = 0; n < displaywidth; n++)
{
writedat(oled_ram[m][n]);
}
}
}
/
prototype : void oled_refreshram_test(void)
parameters : page_start 页开始地址(最小为0)
parameters : page_stop 页结束地址(最大为7)
parameters : column_start 列开始地址(最小为0)
parameters : column_stop 列结束地址(最大为127)
return : none
description : 区域填充
/
void oled_refreshpartram(uint8_t page_start, uint8_t page_stop, uint8_t column_start, uint8_t column_stop)
{
writecmd(0x21); //设置列地址
writecmd(column_start); //列开始地址
writecmd(column_stop); //列结束地址
writecmd(0x22); //设置页地址
writecmd(page_start); //页开始地址
writecmd(page_stop); //页结束地址
for(uint16_t m = page_start; m < (page_stop + 1); m++)
{
for(uint16_t n = column_start; n < (column_stop + 1); n++)
{
writedat(oled_ram[m][n]);
}
}
}
/
prototype : void oled_clearram(void)
parameters : none
return : none
description : 清除数据缓冲区
/
void oled_clearram(void)
{
for(uint16_t m = 0; m < displayheight/8; m++)
{
for(uint16_t n = 0; n < displaywidth; n++)
{
oled_ram[m][n] = 0x00;
}
}
}
/
prototype : void oled_fill(uint8_t fill_data)
parameters : fill_data 填充的1字节数据
return : none
description : 全屏填充 0x000xff
*/
void oled_fullyfill(uint8_t fill_data)
{
for(uint16_t m = 0; m < displayheight/8; m++)
{
for(uint16_t n = 0; n = 0 && x = 0 && y < displayheight) {
if(set_pixel){
oled_ram[y/8][x] |= (0x01 << (y%8));
}
else{
oled_ram[y/8][x] &= ~(0x01 <> (y%8) & 0x01)
return set_pixel;
return reset_pixel;
}
/
prototype : void oled_showstr(int16_t x, int16_t y, uint8_t ch[], uint8_t textsize)
parameters : x,y -- 起始点坐标(x:0127, y:0~63);
ch[] -- 要显示的字符串;
textsize -- 字符大小(1:68 ; 2:816)
return : none
description : 显示codetab.h中的ascii字符,有68和816可选择
/
void oled_showstr(int16_t x, int16_t y, uint8_t ch[], uint8_t textsize)
{
if (x >= 0 && x = 0 && y < displayheight) {
int32_t c = 0;
uint8_t j = 0;
switch(textsize)
{
case 1:
{
while(ch[j] != '�')
{
c = ch[j] - 32;
if(c = 125 || (127-x < 6))//一行最大显示字符数:21字节显示,多出两列,不显示 || 剩余列小于6不能显示完整字符,换行显示
{
x = 0;
y += 8;//换行显示
if(63 - y < 8) // 不足以显示一行时不显示
break;
}
for(uint8_t m = 0; m n) & 0x01);
}
}
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '�')
{
c = ch[j] - 32;
if(c = 127 || (127-x < 8))//16字节显示 || 剩余列小于8不能显示完整字符,换行显示
{
x = 0;
y += 16;//换行显示
if(63 - y < 16) // 不足以显示一行时不显示
break;
}
for(uint8_t m = 0; m < 2; m++)
{
for(uint8_t n = 0; n < 8; n++)
{
for(uint8_t i = 0; i > i) & 0x01);
}
}
}
x += 8;
j++;
}
}break;
}
}
}
/
prototype : void oled_showcn(int16_t x, int16_t y, uint8_t ch)
parameters : x,y -- 起始点坐标(x:0127, y:07);
cn[]:汉字在codetab.h中的索引
return : none
description : 显示codetab.h中的汉字,1616点阵
/
void oled_showcn(int16_t x, int16_t y, uint8_t ch)
{
if (x >= 0 && x = 0 && y = 127 || (127-x < 16))//8个汉字显示||剩余列小于16不能显示完整字符,换行显示
{
x = 0;
y += 16;
if(63 - y < 16) // 不足以显示一行时不显示
break;
}
//需要处理输入数据大于显示数据的问题
for(uint8_t i = 0; i < sizeof(f16x16_cn)/sizeof(gb2312_cn); i++)
{
if(((f16x16_cn[i].index[0] == ch[len]) && (f16x16_cn[i].index[1] == ch[len+1]))){
for(uint8_t m = 0; m < 2; m++) //页
{
for(uint8_t n = 0; n < 16; n++) // 列
{
for(uint8_t j = 0; j > j) & 0x01);
}
}
}
x += 16;
len += 2;
break;
}
else if(f16x16_cn[i].index[0] == ch[len] && ch[len] == 0x20){
for(uint8_t m = 0; m < 2; m++)
{
for(uint8_t n = 0; n < 16; n++)
{
for(uint8_t j = 0; j > j) & 0x01);
}
}
}
x += 16;
len++;
break;
}
}
}
}
}
/ ***
prototype : void oled_show_mixedch(int16_t x, int16_t y, uint8_t* ch)
parameters : x,y -- 起始点坐标(x:0127, y:07); cn[]:汉字在codetab.h中的索引
return : none
description : 显示codetab.h中的汉字,1616点阵,英文,816点阵
/
void oled_showmixedch(int16_t x, int16_t y, uint8_t ch)
{
if (x >= 0 && x = 0 && y = 0xa1)//gb2312从0xa1a0开始
{
for(uint8_t i = 0; i = 127|| (127-x < 16))//8个汉字显示||剩余列小于16不能显示完整字符,换行显示
{
x = 0;
y += 16;
if(63 - y < 16) // 不足以显示一行时不显示
break;
}
for(uint8_t m = 0; m < 2; m++) //页
{
for(uint8_t n = 0; n < 16; n++) //列
{
for(uint8_t j = 0; j > j) & 0x01);
}
}
}
x += 16;
len += 2;
break;
}
}
}
else if(ch[len] <= 127)//ascii编码范围0-127
{
c = ch[len] - 32;
if(c = 127 || (127-x < 8))//16字节显示 || 剩余列小于8不能显示完整字符,换行显示
{
x = 0;
y += 16;
if(63 - y < 16) // 不足以显示一行时不显示
break;
}
for(uint8_t m = 0; m < 2; m++)
{
for(uint8_t n = 0; n < 8; n++)
{
for(uint8_t i = 0; i > i) & 0x01);
}
}
}
x += 8;
len++;
}
}
}
}
/ *****
prototype : void oled_drawbmp(int16_t x0,int16_t y0,int16_t l,int16_t h,const uint8_t bmp[])
parameters : (x0,y0)坐标长l宽h区域绘制图像bmp
0<=x0<=127 0<=y0<=63 0<=l+x0<=127 0<=h+y0= 0 && x0 < displaywidth && x0+l = 0 && y0 < displayheight && y0+h <= displayheight) {
uint8_t *p = (uint8_t *)bmp;
for(int16_t y = y0; y < y0+h; y+=8)
{
for(int16_t x = x0; x i) & 0x01);
oled_setpixel(x, y+i, ((*p) >> i) & 0x01);
}
p++;
}
}
}
}
/***************************************************************
prototype : void oled_areafill(int16_t x0,int16_t y0,int16_t l,int16_t h)
parameters : 区域内容清除,(x0,y0)坐标长l宽h区域
0<=x0<=127 0<=y0<=63 0<=l+x0<=127 0<=h+y0= 0 && x0 < displaywidth && x0+l = 0 && y0 < displayheight && y0+h <= displayheight) {
for(int16_t y = y0; y < y0+h; y++)
{
for(int16_t x = x0; x i) & set_pixel);
}
}
}
oled_refreshram();
}
}
/***************************************************************
prototype : void oled_areaclr(int16_t x0,int16_t y0,int16_t l,int16_t h)
parameters : (x0,y0)坐标长l宽h区域
0<=x0<=127 0<=y0<=63 0<=l+x0<=127 0<=h+y0= 0 && x0 < displaywidth && x0+l = 0 && y0 < displayheight && y0+h <= displayheight) {
for(int16_t y = y0; y < y0+h; y+=8)
{
for(int16_t x = x0; x < x0+l; x++)
{
for(int16_t i = 0; i < 8; i++)
{
oled_setpixel(x, y+i, reset_pixel);
}
}
}
//oled_refreshram();
}
}
/***************************************************************
prototype : void oled_fullytoggle(void)
parameters : none
return : none
description : 缓冲区数据取反后刷新到gddram
***************************************************************/
void oled_fullytoggle(void)
{
for(uint16_t m = 0; m < displayheight/8; m++)
{
for(uint16_t n = 0; n < displaywidth; n++)
{
oled_ram[m][n] = ~oled_ram[m][n];
}
}
oled_refreshram();
}
/***************************************************************
prototype : void oled_areatoggle(int16_t x0,int16_t y0,int16_t l,int16_t h)
parameters : (x0,y0)坐标长l宽h区域
0<=x0<=127 0<=y0<=63 0<=l+x0<=127 0<=h+y0= 0 && x0 < displaywidth && x0+l = 0 && y0 < displayheight && y0+h <= displayheight) {
for(int16_t y = y0; y < y0+h; y+=8)
{
for(int16_t x = x0; x > (y%8) & 0x01) return 1;
return 0;
}
/***************************************************************
prototype : void setpixel_for_scrolldigit(int16_t x, int16_t y, int16_t x, int16_t y, uint8_t set_pixel)
parameters : x
parameters : y
parameters : x
parameters : y
parameters : set_pixel
return : none
description : 设置坐标像素点数据(可以为滚动动画服务)
***************************************************************/
void setpixel_for_scroll(int16_t x, int16_t y, int16_t x, int16_t y, uint8_t set_pixel)
{
if(set_pixel)
{
oled_ram[(y+y)/8][x+x] |= (0x01 << ((y+y)%8));
}
else
{
oled_ram[(y+y)/8][x+x] &= ~(0x01 << ((y+y)%8));
}
}
/****************************************************************************************************************************************************
prototype : void draw_digit_bmp(uint16_t x1, uint16_t y1, const uint8_t bmp[], uint16_t y,uint8_t w, uint8_t h, uint16_t end_line)
parameters : x1 确定图片显示位置(左上角像素点横坐标)
parameters : y1 确定图片显示位置(左上角像素点纵坐标)
parameters : bmp[] 素材图片
parameters : y 所选的一帧图片在素材图片中的纵坐标
parameters : w 素材图片宽度(也是一帧图片的宽度)
parameters : h 一帧图片的高度
parameters : end_line 在素材图片中划出最后一行(用于滚动循环,首尾相接)
return : none
description : 从bmp大图片中获取小图片作为滚动动画的一帧图片
*****************************************************************************************************************************************************/
void draw_bmp_for_scroll(uint16_t x1, uint16_t y1, const uint8_t bmp[], uint16_t y, uint8_t w, uint8_t h, uint16_t end_line)
{
uint16_t x0,y0,y,temp;
for(y = y , y0 = 0 ; y0 end_line) y -= (end_line+1);
for(x0 = 0; x0 < w ; x0++)
{
temp = getpixel_for_scroll(x0, y, bmp, w);
setpixel_for_scroll(x1,y1,x0,y0,temp);
}
}
}
2.屏幕数字滚动
#include
uint8_t hour=0,minute=59,second=55;//时间参数
uint8_t h1,h2,m1,m2,s1,s2;//时钟时分秒六位数字
static uint16_t y1=0,y2=0,y3=0,y4=0,y5=0,y6=0;
static uint8_t temp;
void draw_rolling_clock()
{
switch(h1)
{
case 0:if(y1 < 24*2+1) y1 = 24*2;if(y1 < 24*2+24) y1++;break;
case 1:
case 2:if(y1 h1*24) y1 = h1*24-24;if(y1 < h1*24) y1++;
}
draw_bmp_for_scroll(0, 16, scroll_digit_bmp[0], y1, 20, 24, 2*24+23);//end_line=2*24+23,scroll_digit_small_bmp划到2*24+23行,即0~2
switch(h2)
{
case 0:
{
if(hour == 0) {temp = 3;if(y2 4*24) y2 = 24*3;if(y2 < 24*3+24) y2++;break;}
if(hour == 10 || hour == 20){temp = 9;if(y2 < 24*9+1) y2 = 24*9;if(y2 < 24*9+24) y2++;break;}
}
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:if(y2 h2*24) y2 = h2*24-24;if(y2 < h2*24) y2++;temp = 9;//if(hour == 23) temp = 3;else temp = 9;
}
draw_bmp_for_scroll(22, 16, scroll_digit_bmp[0], y2, 20, 24, temp*24+23);//end_line=temp*24+23,scroll_digit_small_bmp划到temp*24+23行,即0~temp
switch(m1)
{
case 0:if(y3 < 24*5+1) y3 = 24*5;if(y3 < 24*5+24) y3++;break;
case 1:
case 2:
case 3:
case 4:
case 5:if(y3 m1*24) y3 = m1*24-24;if(y3 < m1*24) y3++;
}
draw_bmp_for_scroll(50, 16, scroll_digit_bmp[0], y3, 20, 24, 5*24+23);//end_line=5*24+23,scroll_digit_small_bmp划到5*24+23行,即0~5
switch(m2)
{
case 0:if(y4 < 24*9+1) y4 = 24*9;if(y4 < 24*9+24) y4++;break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:if(y4 m2*24) y4 = m2*24-24;if(y4 < m2*24) y4++;
}
draw_bmp_for_scroll(72, 16, scroll_digit_bmp[0], y4, 20, 24, 9*24+23);//end_line=9*24+23,scroll_digit_small_bmp划到9*24+23行,即0~9
switch(s1)
{
case 0:if(y5 < 16*5+1) y5 = 16*5;if(y5 < 16*5+16) y5++;break;
case 1:
case 2:
case 3:
case 4:
case 5:if(y5 s1*16) y5 = s1*16-16;if(y5 < s1*16) y5++;
}
draw_bmp_for_scroll(94, 24, scroll_digit_small_bmp[0], y5, 14, 16, 5*16+15);//end_line=6*16+15,scroll_digit_small_bmp划到6*16+15行,即0~6
switch(s2)
{
case 0:if(y6 < 16*9+1) y6 = 16*9;if(y6 < 16*9+16) y6++;break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:if(y6 s2*16) y6 = s2*16-16;if(y6 tm_hour;
minute = inow->tm_min;
second = inow->tm_sec;
h1=hour/10; /* 小时的十位 */
h2=hour%10; /* 小时的个位 */
m1=minute/10; /* 分钟的十位 */
m2=minute%10; /* 分钟的个位 */
s1=second/10; /* 秒数的十位 */
s2=second%10; /* 秒数的个位 */
rt_thread_mdelay(1000);
}
}
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 滚动效果线程入口 */
static void thread2_entry(void *param)
{
oled_init();
while(1)
{
/* 每5ms调用一次滚动效果 */
draw_rolling_clock();
rt_thread_mdelay(5);
}
}
int main(void)
{
rt_pin_mode(led_pin, pin_mode_output); //配置为输出模式
/* 创建获取时间的线程,名称是 thread1,入口是 thread1_entry*/
tid1 = rt_thread_create(thread1,
thread1_entry, rt_null,
thread_stack_size,
thread_priority, thread_timeslice);
/* 启动线程1 */
if (tid1 != rt_null)
rt_thread_startup(tid1);
/* 初始化线程 2,名称是 thread2,入口是 thread2_entry */
rt_thread_init(&thread2,
thread2,
thread2_entry,
rt_null,
&thread2_stack[0],
sizeof(thread2_stack),
thread_priority - 1, thread_timeslice);
rt_thread_startup(&thread2);
for (;;)
{
rt_thread_mdelay(1000);
/* 展示时间 */
rt_kprintf(hour:%d,minute:%d,second:%drn,hour, minute, second);
}
}
苹果采用蓝牙技术的无线耳机已在市场占主导地位
SD-WAN是什么,为什么它将会彻底改变现有网络
一文了解智慧物流这一概念
oppor11什么时候上市?oppor11最新消息:最美拍照手机6月发布,跟我一起来体现oppor11带来的黑科技
管道监测压力传感器可减少泄漏造成的环境污染
基于Psoc6开发板的OLED时钟设计方案
工控主板应用范围
苹果Pro Display XDR显示器测试 到底值不值4万元
关于车载双通道USB充电芯片—MP5402M的性能分析和应用介绍
异常处理和错误码管理
汽车遥控门禁系统/遥控车门开关系统简介
深度解读半导体厂商为何对车用芯片虎视眈眈
时钟闹铃控制电路原理图
微软开始为Chromium版Edge浏览器自动更新
高通骁龙435和麒麟655对比评测
影响跌落试验机检测精确性的因素有哪些
波峰焊后出现锡珠的原因是什么?
银锌蓄电池工作原理_银锌蓄电池用途
如何利用体感试衣镜来打造一个不一样的智能试衣间
Instagram开始正式发布其固定评论功能