效果展示
概要
在我们日常生活中经常乘坐电梯,电梯里面和外面都有指示牌,他指示着电梯的楼层和上下移动的方向,人们都根据指示牌来乘坐电梯,电梯的指示牌有数码管和点阵屏的,接下来我们用MCU(51单片机)来控制16x16LED点阵屏,实现电梯指示实验。
我们的16x16LED点阵屏是采用WS2811芯片控制的R、G、B三种颜色LED灯,每一个像素点都包含R、G、B三种颜色。我们怎样来实现以上的效果呢?
本文讲述显示数字取模、函数接口和代码展示等,尽量让读者读懂理解
数字字模表
电梯指示屏显示0-9数字的字模表,字体取模工具生成对应的字模。
8x8数字模表
uint8_t code Digit8x8_tb[][8]=
{
// 1(0) 2(1) 3(2) 4(3) 5(4) 6(5) 7(6) 8(7) 9(8) 0(9)
{0x00,0x00,0x3E,0x41,0x41,0x41,0x3E,0x00},/*"0",0*/
{0x00,0x00,0x00,0x42,0x7F,0x40,0x00,0x00},/*"1",0*/
{0x00,0x00,0x62,0x51,0x49,0x49,0x46,0x00},/*"2",0*/
{0x00,0x00,0x22,0x41,0x49,0x49,0x36,0x00},/*"3",0*/
{0x00,0x00,0x18,0x14,0x12,0x7F,0x10,0x00},/*"4",0*/
{0x00,0x00,0x2F,0x49,0x49,0x49,0x31,0x00},/*"5",0*/
{0x00,0x00,0x3E,0x49,0x49,0x49,0x32,0x00},/*"6",0*/
{0x00,0x00,0x01,0x01,0x79,0x05,0x03,0x00},/*"7",0*/
{0x00,0x00,0x36,0x49,0x49,0x49,0x36,0x00},/*"8",0*/
{0x00,0x00,0x26,0x49,0x49,0x49,0x3E,0x00},/*"9",0*/
{0x00,0x00,0x08,0x04,0x7E,0x04,0x08,0x00},/*"向上",0*/
{0x00,0x00,0x10,0x20,0x7E,0x20,0x10,0x00},/*"向下",0*/
};
8x6数字模表
uint8_t code Digit8x6_tb[][6] =
{
// 1(0) 2(1) 3(2) 4(3) 5(4) 6(5) 7(6) 8(7) 9(8) 0(9)
{0x3E,0x41,0x41,0x41,0x3E,0x00},/*"0",0*/
{0x00,0x42,0x7F,0x40,0x00,0x00},/*"1",0*/
{0x62,0x51,0x49,0x49,0x46,0x00},/*"2",0*/
{0x22,0x41,0x49,0x49,0x36,0x00},/*"3",0*/
{0x18,0x14,0x12,0x7F,0x10,0x00},/*"4",0*/
{0x2F,0x49,0x49,0x49,0x31,0x00},/*"5",0*/
{0x3E,0x49,0x49,0x49,0x32,0x00},/*"6",0*/
{0x01,0x01,0x79,0x05,0x03,0x00},/*"7",0*/
{0x36,0x49,0x49,0x49,0x36,0x00},/*"8",0*/
{0x26,0x49,0x49,0x49,0x3E,0x00},/*"9",0*/
{0x08,0x04,0x7E,0x04,0x08,0x00},/*"向上",10*/
{0x10,0x20,0x7E,0x20,0x10,0x00},/*"向下",11*/
};
程序设计
先前条件
1)我们已将16x16LED点阵屏的驱动写好,驱动程序控制WS2811芯片中的256个像素。
2)内存变量映射:
#define LED_NUM_MAX 256
typedef struct
{
uint8_t Level; // 屏幕亮度
uint16_t Status[LED_NUM_MAX/16]; // 256个灯的状态
uint8_t ColourID[LED_NUM_MAX]; // 256个灯颜色
}RGB_UNIT_T;
// 定义一个变量
RGB_UNIT_T xdata ctl_rgb={0};
ctl_rgb变量将控制 256个像素灯的内存
Level:整个屏幕亮度
Status:映射为256个灯的亮与灭
ColourID:映射为256个灯的颜色ID,颜色ID是指颜色表中的序号
函数接口说明
/*********************************************************************
* 功能: SW2811_SendRGBData()
*
* 注解: 发送R、G、B 3 个字节
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void WS2812_SendRGBData(uint8_t r_Data,uint8_t g_Data,uint8_t b_Data);
/*********************************************************************
* 功能: SW2811_16x16Refresh()
*
* 注解: 16*16 屏刷新输出
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void WS2812_16x16Refresh(void);
/*********************************************************************
* 功能: WS2812_Digit_X_Y_8x8()
*
* 注解: 显示8x8的点阵字模
*
* 输入: x_line : x坐标 0-15 y_line: y坐标 0-15 num: 数字 0-9
*
* 输出: None.
**********************************************************************/
void WS2812_Digit_X_Y_8x8(uint8_t x_line,uint8_t y_line, uint8_t num);
/*********************************************************************
* 功能: WS2812_Digit_X_Y_8x6()
*
* 注解: 显示8x6的点阵字模
*
* 输入: x_line : x坐标 0-15 y_line: y坐标 0-15 ; num : num: 数字 0-9
*
* 输出: None.
**********************************************************************/
void WS2812_Digit_X_Y_8x6(uint8_t x_line,uint8_t y_line, uint8_t num);
/*********************************************************************
* 功能: WS2812_clear_xy_num()
*
* 注解: x_line : x坐标 0-15 y_line: y坐标 0-15 : 清除n 列
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void WS2812_clear_xy_num(uint8_t x_line,uint8_t y_line, uint8_t n);
/*********************************************************************
* 功能: WS2812_MediateDisplayNum()
*
* 注解: 居中显示 2位数字
*
* 输入: 显示 num 楼层数 0-99
*
* 输出: None.
**********************************************************************/
void WS2812_MediateDisplayNum( uint8_t num);
/*********************************************************************
* 功能: SW2811_DisplayUP()
*
* 注解: 向上移动
*
* 输入: x_line : x坐标 0-15 y_line: y坐标 0-15
*
* 输出: None.
**********************************************************************/
void SW2811_DisplayUP(uint8_t x_line,uint8_t y_line, uint8_t num);
/*********************************************************************
* 功能: WS2812_displayWay()
*
* 注解: 电梯方向处理函数
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void WS2812_displayWay(void);
/*********************************************************************
* 功能: SW2811_Init()
*
* 注解: WS2812_初始化
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void WS2812_Init(void);
代码展示
void WS2812_Digit_X_Y_8x8(uint8_t x_line,uint8_t y_line, uint8_t num)
{
uint16_t i,j;
uint8_t *hanzi_tb;
// 获取点阵状态
hanzi_tb = &Digit8x8_tb[num][0];
for( i = 0; i < 8; i++)
{
ctl_rgb.Status[i+x_line] &= ~(0x00FF<<y_line);
ctl_rgb.Status[i+x_line] |= (uint16_t)hanzi_tb[i]<<(y_line);
}
}
void WS2812_Digit_X_Y_8x6(uint8_t x_line,uint8_t y_line, uint8_t num)
{
uint16_t i,j;
uint8_t *hanzi_tb;
uint16_t temp;
// 获取点阵状态
hanzi_tb = &Digit8x6_tb[num][0];
for( i = 0; i < 6; i++)
{
ctl_rgb.Status[i+x_line] &= ~(0x00FF<<y_line);
temp = (uint16_t)hanzi_tb[i]<<y_line;
ctl_rgb.Status[i+x_line] |= temp;
}
// 更新颜色
for(i = 0; i < 6; i++)
{
for(j = 0; j < 8; j++)
{
if(y_line<8)
{
ctl_rgb.ColourID[((i+x_line)<<4) + y_line+j] = 15;
}
else
{
ctl_rgb.ColourID[((i+x_line)<<4) + 8+j] = 15;
}
}
}
}
void WS2812_clear_xy_num(uint8_t x_line,uint8_t y_line, uint8_t n)
{
uint16_t i;
// 获取点阵状态
for( i = 0; i < n; i++)
{
ctl_rgb.Status[i+x_line] &= ~(0x00FF<<y_line);
}
}
void WS2812_MediateDisplayNum( uint8_t num)
{
uint8_t shi;
uint8_t ge;
if(num>99) return;
shi = num/10;
ge = num%10;
if(num < 10)
{
WS2812_clear_xy_num(0,8,16);
WS2812_Digit_X_Y_8x6(6,9,ge); // 显示
}
else
{
WS2812_Digit_X_Y_8x6(3,9,shi);
WS2812_Digit_X_Y_8x6(9,9,ge);
}
}
void SW2811_DisplayUP(uint8_t x_line,uint8_t y_line, uint8_t num)
{
uint16_t i;
uint8_t *hanzi_tb;
uint16_t temp;
if(Displayoffset>7) Displayoffset = 0;
hanzi_tb = &Digit8x6_tb[num][0];
for( i = 0; i < 6; i++)
{
ctl_rgb.Status[i+x_line] &= ~(0x00FF<<y_line);
temp = ((uint16_t)hanzi_tb[i]<<8)+hanzi_tb[i];
ctl_rgb.Status[i+x_line] |= (uint8_t)((temp<<y_line) >> Displayoffset);
}
// 向上移屏
Displayoffset += 1;
}
void WS2812_displayWay( void)
{
//WS2812_Digit_X_Y_8x6(6,0,10);
SW2811_DisplayUP(6,0,10);
}
/*********************************************************************
* 功能: display_handle()
*
* 注解: 显示处理函数
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void display_handle(void)
{
if(timer0_count >= 5)
{ // 100ms cycle
timer0_count = 0;
// 100ms 轮询1次
//HanZi_DisplayLeft();
time_count++;
if(time_count>=8)
{
time_count = 0;
floor_level ++;
if(floor_level > 33)
{
floor_level = 0;
}
WS2812_MediateDisplayNum(floor_level); // 显示楼层数字
}
WS2812_displayWay(); // 显示箭头方向
}
}
/*********************************************************************
* 功能: display_run()
*
* 注解: 显示刷新函数 timer0_OutTime_Flag 20 ms周期
*
* 输入: None.
*
* 输出: None.
**********************************************************************/
void display_run(void)
{
uint16_t xdata i;
uint8_t xdata r_Data;
uint8_t xdata g_Data;
uint8_t xdata b_Data;
uint8_t xdata data1,data2;
if(timer0_OutTime_Flag)
{ // 20ms cycle
timer0_OutTime_Flag = 0;
//uart_printf("20ms cycle");
WS2812_16x16Refresh(); // 刷新显示
if(COM1.RX_TimeOut > 0) // uart rx
{
if(--COM1.RX_TimeOut == 0)
{
uart_rx_handle(RX1_Buffer,COM1.RX_Cnt);
COM1.RX_Cnt = 0;
}
}
}
}
显示处理函数和显示刷新函数 中,timer0_count和timer0_OutTime_Flag 是定时器0中断 以20ms为周期的
/********************* Timer0中断函数************************/
void timer0_int (void) interrupt TIMER0_VECTOR
{
timer0_OutTime_Flag = 1;
timer0_count ++;
}
小结
电梯楼层指示屏程序开发,还挺考验程序员逻辑思维的,这里需要掌握以下3方面知识:①学会显示字体进行取模 ②C语言功底要好,学会C语言拼字、组合、位运算等 ③ 需要构造不同需求的函数接口,如在x和y坐标显示数字。以上是主要电梯楼层指示屏程序,对此您有何评论?