第九课、数码管
课程主要内容:
本视频介绍了8051U单片机与数码管的应用。首先,讲解了数码管的基本类型和工作原理,包括共阴与共阳的连接方式。接着,演示了如何通过595移位寄存器控制数码管的显示,实现静态和动态显示数字。视频中还分享了如何使用虚拟显示工具进行调试和显示,以及如何编写相关代码。
学习感悟:
数码管的共阴、共阳要分清。595的功能就是译码。11脚的SCK是时钟;12脚的RCK是锁存寄存器;14脚SER数据输入,上一片的9脚数据输出到它可以无限连接下去;15,1~7脚数据并行输出。时序图是从左往右走,本课中数据是从高位先传,低位数据后传。字库生成工具中有数码管的快捷生成功能。有了问题要多看资料,多看例程,比如我在Send_595这个函数中,错把dat<<=1写成dat<<=i,就是通过学习实验箱例程发现问题的。LED40_SendData函数的声明在stc32_stc8_usb.h文件中,如果需要使用调试仿真接口就需要调用它。这个仿真调用感觉就是通过MCU相应的串口不停向上位机传送数据,上位机接收到数据后通过软件模拟显示相应的内容。这种方式首先要占用一个串口,而且容易掉数据。我将视频中的例子运行时就看见,有时仿真的数字是跳着走,而仿真箱中的数字则一直很稳定。
作业:
简易10秒免单计数器
1、在前四位数码管上显示目标时间,即“10.00”表示定时时间10秒钟。
2、后四位显示当前的计时00.00,最小单位为10ms。
3、按下开始按钮后,每10ms最末尾的数字+1;直到按下结束按钮后停止计数。
下面的程序按下P34键后将进入计数,如果是在9990ms与10009ms之间将认为成功,而后程序进入数码管流水灯闪烁以表示胜利等待重新启动。
io.c
#include "io.h"
bit pauseTag = 1; //暂停计数标志位
bit winTag = 0; //胜利标志位
bit blinkTag = 0; //闪烁标志位
u8 Seg_no = 0; //段码位置
u16 Key_Vol = 0; //按钮延时时间
u16 timeVol = 0; //计时计数
u8 SEG_NUM[]= //段码字库
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x40, /*'-', 10*/
0x00, /*' ', 11*/
0x80, /*'.', 12*/
};
u8 T_NUM[]= //位码,使用时要取反
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
void Init_595(void)
{
HC595_SER = 0;
HC595_RCK = 0;
HC595_SCK = 0;
}
void Send_595(u8 dat)
{
u8 i;
for(i = 0;i < 8;i++)
{
dat <<= 1; //溢出位到CY
HC595_SER = CY; //数据输入
HC595_SCK = 1; //产生上升沿
HC595_SCK = 0; //复位
}
}
void Display_Seg(u8 HC595_1,u8 HC595_2)
{
Send_595(HC595_1); //段码输出,低电平点亮
Send_595(HC595_2); //位码输出,高电平点亮
HC595_RCK = 1; //数据输出
HC595_RCK = 0; //复位
}
void blinkDisplay(void) //流水灯闪烁显示
{
if(winTag == 1)
{
if(blinkTag == 0)
{
u16 num = 0;
if(Seg_no == 0)
{
Display_Seg(SEG_NUM[1],~T_NUM[Seg_no]); //第一位显示固定的“1”
}
else if(Seg_no == 1)
{
Display_Seg(SEG_NUM[0] + SEG_NUM[12],~T_NUM[Seg_no]); //第二位显示“0+小数点”
}
else if(Seg_no == 2)
{
Display_Seg(SEG_NUM[0],~T_NUM[Seg_no]); //第三位显示固定的“0”
}
else if(Seg_no == 3)
{
Display_Seg(SEG_NUM[0],~T_NUM[Seg_no]); //第四位显示固定的“0”
}
else if(Seg_no == 4)
{
Display_Seg(SEG_NUM[1],~T_NUM[4]); //第五位显示固定的“1”
}
else if(Seg_no == 5)
{
Display_Seg(0xBF,~T_NUM[5]); //第六位显示,0xBF为段码的“0+小数点”
}
else if(Seg_no == 6)
{
Display_Seg(SEG_NUM[0],~T_NUM[6]); //第七位显示固定的“0”
}
else if(Seg_no == 7)
{
Display_Seg(SEG_NUM[0],~T_NUM[7]); //第八位显示固定的“0”
}
Seg_no++;
if(Seg_no > 7)
Seg_no = 0;
blinkTag = 1;
}
else
{
char i;
for(i=0; i<8; i++)
{
Display_Seg(0x00,~T_NUM[0]); //清空显示
}
blinkTag = 0;
}
}
}
void Seg_Task(void)
{
if(winTag == 0)
{
u16 num = 0;
if(Seg_no == 0)
{
Display_Seg(SEG_NUM[1],~T_NUM[Seg_no]); //第一位显示固定的“1”
}
else if(Seg_no == 1)
{
Display_Seg(SEG_NUM[0] + SEG_NUM[12],~T_NUM[Seg_no]); //第二位显示“0+小数点”
}
else if(Seg_no == 2)
{
Display_Seg(SEG_NUM[0],~T_NUM[Seg_no]); //第三位显示固定的“0”
}
else if(Seg_no == 3)
{
Display_Seg(SEG_NUM[0],~T_NUM[Seg_no]); //第四位显示固定的“0”
}
else if(Seg_no == 4)
{
num = timeVol/10000; //取五位上的数值
Display_Seg(SEG_NUM[num],~T_NUM[Seg_no]);
}
else if(Seg_no == 5)
{
num = (timeVol/1000)%10; //取六位上的数值
Display_Seg(SEG_NUM[num] + SEG_NUM[12],~T_NUM[Seg_no]); //显示“数值+小数点”
}
else if(Seg_no == 6)
{
num = (timeVol/100)%10; //取七位上的数值
Display_Seg(SEG_NUM[num],~T_NUM[Seg_no]);
}
else if(Seg_no == 7)
{
num = (timeVol/10)%10; //取八位上的数值
Display_Seg(SEG_NUM[num],~T_NUM[Seg_no]);
}
Seg_no++;
if(Seg_no > 7)
Seg_no = 0;
}
}
void TIMECOUNT_Task(void)
{
if(pauseTag == 0)
{
timeVol++;
if(timeVol > 10009) //大于10000ms清零
timeVol = 0;
}
}
void KEY_Task(void)
{
if(P34 == 0)
{
Key_Vol++;
if(Key_Vol == 5) //延时5ms
{
if(winTag == 0) //没有胜利继续游戏
{
if(pauseTag == 1)
{
pauseTag = 0; //开始计时
}
else
{
pauseTag = 1;
if(timeVol > 9990 && timeVol <10009) //如果水平不够可以调小第一个数值,我就是调小测试的。
winTag=1; //进入胜利状态
}
}
}
}
else
{
Key_Vol = 0;
}
}
task.c
#include "task.h"
static TASK_COMPONENTS Task_Comps[]=
{
//状态(0不执行/1执行) 计数(计数置进行递减,为0时将状态位置1) 周期(等计数位为0时重新将本位的数赋值给计数位) 函数(要执行的函数名称)
{0, 1, 1, KEY_Task}, //检测开始按键是否按下,本程序为P35
{0, 1, 1, Seg_Task}, //LED显示刷新
{0, 1, 1, TIMECOUNT_Task}, //延时计数
{0, 100, 100, blinkDisplay}, //胜利后数码管以流水灯形式闪烁显示
};
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]); //计算数组有多少元素,在这段程序中就是计算有多少条任务
//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
// 注意:在主程序的时间回调函数中执行即可控制时间频率,本程序就是1ms执行一次。
//========================================================================
void Task_Marks_Handler_Callback(void) //计数位进行递减到0时将Run的状态置1
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps[i].TIMCount) /* If the time is not 0 */
{
Task_Comps[i].TIMCount--; /* Time counter decrement */
if(Task_Comps[i].TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps[i].TIMCount = Task_Comps[i].TRITime;
Task_Comps[i].Run = 1; /* The task can be run */
}
}
}
}
//========================================================================
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
// 注意:在主程序的循环体中执行即可判断何时对指定任务的调用。
//========================================================================
void Task_Pro_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps[i].Run) /* If task can be run */ //如果Run的状态为1(真),就执行函数
{
Task_Comps[i].Run = 0; /* Flag clear 0 */
Task_Comps[i].TaskHook(); /* Run task */
}
}
}