不错不错,后面的程序怎么才能写成这种格式
先发文,然后选择编辑,这时候上面的编辑条中选择插入代码,然后将代码粘贴进去。 严西湖里的鱼 发表于 2024-12-15 20:53
先发文,然后选择编辑,这时候上面的编辑条中选择插入代码,然后将代码粘贴进去。 ...
好的 我去试试 太强了 <p>感谢提供的数码管作业的思路,已完成课后作业</p>
第十课、虚拟LED和数码管
课程主要内容:
本次课程专注于教授如何利用虚拟软件进行实验和调试,特别针对没有实验箱或最小系统版的学员。讲师首先介绍了虚拟软件的强大功能,包括虚拟显示、键盘以及多种调试工具,并强调了软件(最新ISP软件)与硬件(擎天柱)的准备。课程内容涵盖使用虚拟软件控制LED显示,通过定义变量来操作P2端口的流水灯和P10端口的闪烁灯,以及控制虚拟数码管显示字符串、整数和浮点数。此外,还讲解了如何通过虚拟键盘接收按键输入并控制数码管显示输入的数字。并鼓励学生课后练习密码锁项目,以深化对虚拟调试与实验的理解。
学习感悟:
LED40_SendData()为所有管脚;LED40_SetPort()为修改单独一组管脚;LED40_SetBit()为单独一个管脚;LED_ClrBit()设置单独一个管脚为零(清零);LED_SetBit()设置单独一个输出高电平(置一)。在ISP中的“调试仿真接口”下的“接口协议及帮助”中也有详细介绍。命令头中的“4C 45 44 28”实际上就是ASCII码中的“LED(”。stc32_stc8_usb.h在新的版本中已经修改为Ai8051.h,相应的调用都要修改。在第二个例子中KEY1_COUNT必须申明为u32(long)类型,否则在程序运行的过程中只要大于6次就会显示乱码。根据程序分析因为要将其乘以10000,这时因为u16(int)类型最大是65535,所以产生了溢出。也根据这一情况来开,在进行运算时,如果你声明一个类型,它的运算结果并不会直接放到被赋值的空间中去,而是暂时存在一个与你当前数据类型相同的一个临时空间中,如果空间不够就会产生溢出。在使用软件仿真的数码管编程时发现其不支持显示超过F字符的字符(OPEN只能显示E字符),只好又利用实验箱中的数码管进行程序的编写。效果和程序完全是两码事,感觉有机会大家最好还是要在硬件上编写测试为好。
课后小练:
密码锁
1.没有输入时,显示“--------“
2.有输入时,按下一个按键,开始按顺序写入
例如,第一个按下 1,显示“1-------”
例如,第二个按下 3,“显示“13------“
3.当按下的密码为“12345678”时,数码管显示 open 的字符,否则(输入8位错误的),还是显示“--------“。
main.c
#include <config.h>
#include "task.h"
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
extern bit receiveData; //跨文件(io.c)调用
extern u32 REC_NUM; //跨文件(io.c)调用
void main()
{
Sys_init(); //系统初始化
usb_init(); //USB口初始化配置
IE2 |= 0x80; //EUSB位置位,打开USB
EA = 1; //总中断置位,打开中断
Timer0_Init(); //开始1ms延时
while(DeviceState != DEVSTATE_CONFIGURED); //等待USB完成配置
while(1)
{
if (bUsbOutReady) //如果接收到了数据
{
if(UsbOutBuffer >= '0' && UsbOutBuffer <= '9')
{
REC_NUM = UsbOutBuffer - '0'; //接收串口键盘的数据,将ASCII码的字符变换为数值赋值
receiveData = 1; //有效接收
}
else
{
receiveData = 0; //无效接收
}
usb_OUT_done(); //清除当前接收区
}
Task_Pro_Handler_Callback(); //如果Run的状态为1(真),就执行函数
}
}
void Timer0_Isr(void) interrupt 1 //1ms执行一次
{
Task_Marks_Handler_Callback(); //计数位进行递减到0时将Run的状态置1,在这里就是1ms减1
}
io.c
#include "io.h"
bit receiveData = 0; //是否接收新字符,0为未接收,1为已接收;main.c中调用此参数
bit openTag = 0; //开锁状态,0为关,1为开
u32 keyCount = 0; //按键计数
u32 REC_NUM; //接收的字符;main.c中调用此参数
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*/
0x79, /*'E', 10*/
0x54, /*'N', 11*/
0x5C, /*'O', 12*/
0x73, /*'P', 13*/
0x40, /*'-', 14*/
0x00, /*' ', 15*/
};
u8 T_NUM[]= //位码,使用时要取反
{
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
};
//填充初始值为“--------”,此为段码显示
u8 fmt = {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40};
//填充初始值为“12345678”,修改此处即可修改开锁密码,此为段码显示
u8 passwordValue = {0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F};
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; //复位
}
bit areArraysEqual(unsigned char arr1[],unsigned char arr2[]) { //比较数组是否相等,不等返回0,相等返回1
u8 tempVol1;
for (tempVol1 = 0; tempVol1 < 8; tempVol1++) {
if (arr1 != arr2) {
return 0;
}
}
return 1;
}
void KEY_Task(void)
{
u8 i;
if(receiveData == 1 && openTag == 0) //比较是否接收到新字符,并且开锁状态为关
{
fmt = SEG_NUM;
keyCount++;
if(keyCount == 8)
{
keyCount = 0;
if(areArraysEqual(fmt,passwordValue))
{
openTag = 1;
fmt = SEG_NUM; //显示"-"
fmt = SEG_NUM; //显示"-"
fmt = SEG_NUM; //显示"O"
fmt = SEG_NUM; //显示"P"
fmt = SEG_NUM; //显示"E"
fmt = SEG_NUM; //显示"N"
fmt = SEG_NUM; //显示"-"
fmt = SEG_NUM; //显示"-"
}
else
{
for(i = 0;i < 8;i++)
fmt = SEG_NUM; //全部显示"-"
}
}
receiveData = 0;
}
else
receiveData = 0;
}
//任务:虚拟键盘上按下数字按键在数码管显示出来
void TASK_Display(void)
{
u8 j;
for(j = 0;j < 8;j++)
Display_Seg(fmt,~T_NUM);
}
程序运行的倒是挺好,不知道为啥最后一位横线那么亮?{:yiwen:}
上面的问题解决了,应该是用for循环的话,每次显示的时间太短了,其它的数码管就会显示的偏暗,将其改为if语句就好了。
u8 Seg_no = 0;
void TASK_Display(void)
{
/*
显示偏暗的语句
u8 j;
for(j = 0;j < 8;j++)
Display_Seg(fmt,~T_NUM);
*/
if(Seg_no == 0)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 1)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 2)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 3)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 4)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 5)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 6)
{
Display_Seg(fmt,~T_NUM);
}
else if(Seg_no == 7)
{
Display_Seg(fmt,~T_NUM);
}
Seg_no++;
if(Seg_no > 7)
Seg_no = 0;
}
第十一课、矩阵按键
课程主要内容:
本次课程从8051U系列单片机的入门知识开始,逐步深入至32位单片机的实战操作。重点转向探讨了矩阵按键的工作原理,相比独立按键,矩阵按键能更高效地利用端口资源。通过交替扫描行和列,课程详细讲解了矩阵按键的电路设计、编程实现方法,包括端口定义、按键检测代码编写,以及利用数码管显示按键编号的技术。此外,讲师还预告了后续课程内容,包括密码锁和简易洗衣机等实验任务,鼓励学生积极参与并分享课后任务成果于论坛。
学习感悟:
按键识别原理:端口默认为高电平,实时读取到引脚为低电平是表示按下。
①第一步:现将 P0.0-P0.3 输出低电平,P0.6-P0.7 输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
②第二步:现将 P0.0-P0.3 输出高电平,P0.6-P0.7 输出低电平,如果有按键按下,按下的那一列的 IO 就会变成低电平,就可以判断出哪一列按下了。
③第三步:行列组合一下就可以判断出是哪个按键按下了。
“extern”关键字在声明的变量前使用可以跨文件调用,不得加赋值。
课后小练:
简易洗衣机面板
1、按下开机键后,数码管显示 1,表示默认为清洗模式 1;
2、用矩阵按键模拟洗衣机的操作面板,前五个按键模拟 1-5 的清洗模式按键,选择几的时候数码管显示数字几,表示以当前为第几个功能;
3、按下启动后,按照选择的模式对应的时间开始倒计时,倒计时结束后,数码管熄灭,表示清洗完成(清洗时间自己随意设置)。
在本程序中设各按键如下:
0:开机
1:常用 //定时10秒
2:标准 //定时15秒
3:快洗 //定时5秒
4:毛毯 //定时30秒
5:风干 //定时20秒
6:启动
io.c
#include "io.h"
bit timeStartTag = 0; //开始计数标志位,0为不计数,1为计数
bit turnOnTag = 0; //开机标志位,0为关机,1为开机
bit workOrTimeTag = 0; //工作或时间标志位,0为工作模式,1为记时模式
u8 Seg_no = 0; //段码位置
u16 Key_Vol = 0; //按钮延时时间
u16 timeVol = 0; //计时计数
u8 key_no = 0; //定义密码的位置值
u8 workingMode = 11; //定义密码初始值,11对应段码的空字符
u8 key_num = 11; //定义按键的初始值,11对应段码的空字符
/*
key_num作为功能判断,其值:
0:开机
1:常用模式,定时10秒
2:标准模式,定时15秒
3:快洗模式,定时5秒
4:毛毯模式,定时30秒
5:风干模式,定时20秒
6:启动
*/
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; //复位
}
/*
端口定义
#define ROW1 P06
#define ROW2 P07
#define COL1 P00
#define COL2 P01
#define COL3 P02
#define COL4 P03
*/
void Task_1(void)
{
//①第一步:现将 P0.0-P0.3 输出低电平,P0.6-P0.7 输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
if(ROW1 == 0 || ROW2 == 0) //如果行有按键按下
{
if(ROW1 == 0 && ROW2 == 0) //如果同时按下不处理
{
}
else if((ROW1 == 1 && ROW2 == 0) || (ROW1 == 0 && ROW2 == 1)) //有单一按键按下
{
if(ROW1 == 1) //如果是第一行就赋值为0
{
key_num = 0;
}
else
{
key_num = 4; //如果是第二行就赋值为4
}
//②第二步:现将 P0.0-P0.3 输出高电平,P0.6-P0.7 输出低电平,如果有按键按下,按下的那一列的 IO 就会变成低电平,就可以判断出哪一列按下了。
COL1 = 1;
COL2 = 1;
COL3 = 1;
COL4 = 1;
ROW1 = 0;
ROW2 = 0;
//③第三步:行列组合一下就可以判断出是哪个按键按下了。
if(COL1 == 0) //判断那一列按下
{
//加零毫无意义
}
else if(COL2 == 0)
{
key_num += 1;
}
else if(COL3 == 0)
{
key_num += 2;
}
else if(COL4 == 0)
{
key_num += 3;
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
}
else
{
key_num = 11;
}
}
//LED显示刷新
void SEG_Task(void)
{
//只需使用两位数码管,其它位全部为空白。
switch(Seg_no)
{
case 0:
Display_Seg(SEG_NUM,~T_NUM);
break;
case 1:
Display_Seg(SEG_NUM,~T_NUM);
break;
case 2:
Display_Seg(SEG_NUM,~T_NUM); //11对应段码的空字符
break;
case 3:
Display_Seg(SEG_NUM,~T_NUM);
break;
case 4:
Display_Seg(SEG_NUM,~T_NUM);
break;
case 5:
Display_Seg(SEG_NUM,~T_NUM);
break;
case 6:
Display_Seg(SEG_NUM,~T_NUM); //如果是计时模式就显示定时值的十位数,否则显示空字符
break;
case 7:
Display_Seg(SEG_NUM,~T_NUM); //如果是计时模式就显示定时值的个位数,否则显示workingMode的值
}
Seg_no++;
if(Seg_no > 7)
Seg_no = 0;
}
void TIMECOUNT_Task(void)
{
if(timeStartTag == 1)
{
timeVol--;
if(timeVol == 0)
{
timeStartTag = 0; //停止计时
workOrTimeTag = 0; //切换到工作模式状态
workingMode = 11; //清屏
turnOnTag = 0; //关机
}
}
}
//判断按下的数是那种工作模式
void PW_write_Task(void)
{
if(key_num < 11)
{
Key_Vol++;
if(Key_Vol == 1) //延时10ms
{
switch(key_num)
{
case 0:
if(turnOnTag == 0) //开机,本条case0没有break,和下面的case1语句共同执行
turnOnTag = 1;
case 1: //1:常用模式,定时10秒
if(turnOnTag == 1)
{
workingMode = 1; //与case0共用本条,开机与设置为常用模式都置1
timeVol = 10000;
}
break;
case 2: //2:标准模式,定时15秒
if(turnOnTag == 1)
{
workingMode = key_num;
timeVol = 15000;
}
break;
case 3: //3:快洗模式,定时5秒
if(turnOnTag == 1)
{
workingMode = key_num;
timeVol = 5000;
}
break;
case 4: //4:毛毯模式,定时30秒
if(turnOnTag == 1)
{
workingMode = key_num;
timeVol = 30000;
}
break;
case 5: //5:风干模式,定时20秒
if(turnOnTag == 1)
{
workingMode = key_num;
timeVol = 20000;
}
break;
case 6: //启动模式
if(turnOnTag == 1)
{
timeStartTag = 1; //定时器开始工作
workOrTimeTag = 1; //切换到计时工作状态
}
break;
default: //其它情况默认不执行直接退出,比如按下按键7
break;
}
}
Key_Vol = 0;
}
else
{
Key_Vol = 0;
}
} 第十二课、复位系统
课程主要内容:
本视频讲解了Ai8051U单片机的复位系统,包括上电复位、低压复位、复位引脚、看门狗复位和软件复位的实现及其重要性。复位系统确保设备在异常状态下恢复正常运行,避免不确定行为和数据错误。视频中还提供了编写看门狗程序的示例,强调在系统失控时及时复位的重要性,并介绍了如何通过USB进行下载模式切换。最后,提出了课后练习以巩固所学内容。
学习感悟:
上电复位使用较长延时,这个“较长”也就100多毫秒。低压复位检测的是VCC引脚,并且可以调整复位检测电压。如果不选择低压复位将会产生低压中断,但是在这种情况下不建议操作掉电保存的数据,可以设置信号灯告知电压过低。复位引脚是低电平复位,使用前要打开。看门狗复位实际上就是一个自动计数器,要不停清零(CLR_WDT置1),否则会触发复位。
页:
1
[2]