cbai
发表于 2025-4-8 19:19:09
cbai 发表于 2025-4-3 14:28
第七集学习心得:这一集主要讲了8051单片机的定时器的中断,现实中的中断和单片机的中断有点类似,就是正 ...
第八集学习心得:这一集开始老师教大家找keil编译错误,一般找报错的上面行是否有错误;并且使用一个定时器实现三个不同时间的周期性任务,可以以基准时间定义定时器,给每个周期任务定义一个计数值,对计数值进行判断后单独写各自的逻辑;定义数组是类型 数组名[数组长度],可以定义的时候赋初值,数组索引从0开始;利用所学知识实现LED灯流水灯;后面讲到了结构体定义业务环境还是有点难的,因为不知道业务使用的结构体改定义哪些成员变量,封装自己的业务功能为.c和.h文件就更难了,不知道具体业务逻辑的话很难写全,总之后面的部分还是有点难理解的。加油!
cbai
发表于 2025-4-9 10:05:11
cbai 发表于 2025-4-8 19:19
第八集学习心得:这一集开始老师教大家找keil编译错误,一般找报错的上面行是否有错误;并且使用一个定时 ...
流水灯逻辑
#include "ai8051u.h"
#include "intrins.h"
#include "stc32_stc8_usb.h"
#include "stdio.h"
/*
任务2:数组点亮LED,实现流水灯 0点亮1熄灭
*/
/*
LED1 LED2 LED3 LED4 LED5 LED6 LED7 LED8
0 1 1 1 1 1 1 1
1 0 1 1 1 1 1 1
1 1 0 1 1 1 1 1
1 1 1 0 1 1 1 1
1 1 1 1 0 1 1 1
1 1 1 1 1 0 1 1
1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 0
*/
// 实现思路:每500毫秒,按照上面的矩阵电平赋值给相应的LED灯
#define u8 unsigned char
#define LED_NUM 8
u8 Led_state = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; // 因为P0口能控制8个LED的高低电平,每隔500ms给P0赋值数组中的元素,巧妙使用规律加位操作实现流水灯
void Timer0_Init(void) //500毫秒@24.000MHz
{
TM0PS = 0x0F; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xDC; //设置定时初始值
TH0 = 0x0B; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
u8 i = 0;
void main(void) {
int flag = 0;
int bonus = 0;
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
// 按照原理图,点亮8个LED灯需要按下P40总开关和给8个LED灯低电平
// 1、相应的GPIO口初始化
P0M1 = 0; P0M0 = 0;
P1M1 = 0; P1M0 = 0;
P2M1 = 0; P2M0 = 0;
P3M1 = 0; P3M0 = 0;
P4M1 = 0; P4M0 = 0;
P5M1 = 0; P5M0 = 0;
P6M1 = 0; P6M0 = 0;
P7M1 = 0; P7M0 = 0;
usb_init();
IE |= 0x80; // 允许USB中断
EA = 1; // 开启中断总开关
P40 = 0;
// 2、P40低电平
// 3、P00 P01 P02 P03 P04 P05 P06 P07低电平
while(1) {
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
// printf_usb("1. Read Num:%d\n", OutNumber);
// printf_usb("2. Read Num:%d\n", OutNumber);
// printf_usb("3. Read Num:%d\n", OutNumber);
// printf_usb("4. Read Num:%d\n", OutNumber);
usb_OUT_done();
}
P0 = ~Led_state;
if(i > 7){
i = 0;
}
Timer0_Init(); // 定时器初始化
}
}
void Timer0_Isr(void) interrupt 1
{
i++;
}
cbai
发表于 2025-4-9 17:59:23
cbai 发表于 2025-4-9 10:05
流水灯逻辑
#include "ai8051u.h"
#include "intrins.h"
第九集学习心得:看懂单片机上数码管芯片的原理图和引脚接法,注意时共阴极数码管还是共阳极数码管,这关乎到显示是给高电平还是低电平;数码管时串行输入并行输出芯片,注意输出条件,一般需要锁存几位然后发送信号并行输出数据;后面动态显示主要利用人的视觉暂留,快速刷新位码和段码数据,还是要理解数码管的原理。
cbai
发表于 2025-4-15 14:43:56
cbai 发表于 2025-4-9 17:59
第九集学习心得:看懂单片机上数码管芯片的原理图和引脚接法,注意时共阴极数码管还是共阳极数码管,这关 ...
#include "io.h"
u16 Key_Vol = 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*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};
u8 SEG_ID[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; //数码管位码,哪些数码管会显示字符
void LED0_Blink(void)//点亮LED1
{
P00 = !P00;
}
void LED1_Blink(void)//点亮LED2
{
P01 = !P01;
}
void LED2_Blink(void)//点亮LED3
{
P02 = !P02;
}
void KEY_Task(void) //判断按键次数
{
if(P32 == 0){
Key_Vol++;
if(Key_Vol == 5){
//按键按下后的任务
}
}
else{
Key_Vol = 0;
}
}
// 默认上电后595全部是高电平,初始化为低电平
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++)
{
u8 yichu = 0;
yichu = dat >> 7;// 这两行是为了验证CY就是dat左移1位后溢出的高位值
dat <<= 1; //高位丢弃的位给CY
// HC595_SER = CY;//先把数据写到引脚上
HC595_SER = yichu;
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;
}
u8 hour = 0;
u8 minute = 0;
u8 second = 0;
u8 seg_idx = 0;
void Seg_task(void)
{
u8 num = 0;
if(seg_idx == 0){
num = hour / 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 1){
num = hour % 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 2){ //显示连接符"-"
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 3){
num = minute / 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 4){
num = minute % 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 5){ //显示连接符"-"
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 6){
num = second / 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
else if(seg_idx == 7){
num = second % 10;
Display_Seg(SEG_NUM, ~SEG_ID);
}
seg_idx++;
if(seg_idx > 7){
seg_idx = 0;
}
}
void TimeCount_task(void){
second++;
if(second > 59){
minute++;
second = 0;
}
else if(minute > 59){
hour++;
minute = 0;
}
else if(hour > 23){
hour = 0;
}
}
数码管显示“00-00-00”分别代表时分秒 ,每过1秒钟秒+1主代码逻辑
cbai
发表于 2025-4-16 13:55:51
cbai 发表于 2025-4-15 14:43
#include "io.h"
第十集学习心得:课程主要讲了虚拟显示LED、数码管和虚拟键盘,都是利用ISP软件模拟出来的
调试仿真接口,可以利用菜单栏“调试仿真接口中的接口协议及帮助”选项学习接口协议,利用已有的API并在keil中导入最新版的usb库文件或者相应api函数原型。主要用到了DIP40封装LED接口。理解每个接口的作用和参数,能利用led、数码管和按键写程序,结合isp软件模拟控制和输出。
cbai
发表于 2025-4-17 11:36:05
第十一集学习心得:主要讲了独立按键和矩阵按键的原理,通过原理图,查看按键按下和抬起的高低电平变化,矩阵按键可以利用有限的IO口控制多个按键,提高了IO口的利用率;后面通过矩阵按键的程序和密码锁的案例讲了矩阵按键的应用;
//任务一:数码管显示当前的按键号
u8 key_num = 0xff;
//任务1:数码管显示当前的按键号
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 == 0) && (ROW2 == 1)) || ((ROW2 == 0) && (ROW1 == 1))){
if(ROW1 == 0){
key_num = 0;
}
else if(ROW2 == 0){
key_num = 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){
key_num = key_num;
}
else if(COL2 == 0){
key_num = key_num + 1;;
}
else if(COL3 == 0){
key_num = key_num + 2;;
}
else if(COL4 == 0){
key_num = key_num + 3;;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else{
key_num = 0xff;
}
//③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
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; //DAT = (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 Seg_task(void){
if(key_num == 255){
Display_Seg(SEG_NUM, ~SEG_ID);
}
else{
Display_Seg(SEG_NUM, ~SEG_ID);
}
}
上述程序主逻辑就是判断矩阵按键是否按下并在数码管上显示按键所对应的按键值
cbai
发表于 2025-4-17 15:55:46
cbai 发表于 2025-4-17 11:36
第十一集学习心得:主要讲了独立按键和矩阵按键的原理,通过原理图,查看按键按下和抬起的高低电平变化,矩 ...
//任务1:数码管显示当前的按键号
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 ==0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if( ROW2 ==0 )
key_num = 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 ) //判断哪一列,叠加按键的序号
{
// key_num = key_num ;
}
else if( COL2 ==0 )
{
key_num = key_num + 1;
}
else if( COL3 ==0 )
{
key_num = key_num + 2;
}
else if( COL4 ==0 )
{
key_num = key_num + 3;
}
}
COL1 = 0;
COL2 = 0;
COL3 = 0;
COL4 = 0;
ROW1 = 1;
ROW2 = 1;
}
else
{
key_num = 0xff;
}
//③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
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; //DAT = (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 SEG_Task(void)
//{
// if( key_num == 255 )
// Display_Seg( SEG_NUM , ~T_NUM); //数码管刷段码和位码
// else
// Display_Seg( SEG_NUM , ~T_NUM); //数码管刷段码和位码
//}
u8 passward = { 16,16,16,16,16,16,16,16 };
u8 Seg_no = 0;
void SEG_Task(void)
{
u8 num = 0;
if( Seg_no ==0 ) //小时十位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==1 ) //小时的个位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==2 ) //第一个横杠
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==3 ) //分钟的十位
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==4 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==5 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==6 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else if( Seg_no ==7 )
{
Display_Seg( SEG_NUM] , ~T_NUM); //数码管刷段码和位码
}
else
{
}
Seg_no ++;
if( Seg_no>7 )
Seg_no=0;
}
u8 Key_Vol3 = 0;
u8 key_no =0 ;
void PW_write_Task(void)
{
if( key_num <0xff )
{
Key_Vol3 ++;
if( Key_Vol3 == 5 )
{
if( key_no == 0 )
{
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
}
passward[ key_no] = key_num ;
key_no ++;
// passward = 17;
if( key_no == 8 ) //密码输入到了八位
{
if((passward==1) && (passward==2) && (passward==3) && (passward==4) && (passward==5) && (passward==6) && (passward==7) &&(passward==0) )
{
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 17;
passward = 1;
}
else
{
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
passward = 16;
}
key_no = 0;
}
}
}
else
{
Key_Vol3 = 0;
}
}
补充密码锁主逻辑代码
cbai
发表于 2025-4-17 17:53:35
第十二课学习心得:开头讲了复位系统的使用时机和作用,后面结合ai8051u单片机的硬件复位系统和软件复位系统,模拟演示了硬件看门狗复位程序和软件复位程序;软硬件复位程序中如果有USB功能需要先加USB复位程序
void USB_Reset_usb(void){
// 程序有USB功能记得先加USB复位
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
USBCON = 0X00;
USBCLK = 0X00;
IRC48MCR = 0X00;
Delay10ms();
}
然后在硬件看门狗复位程序中按照寄存器的位给值,看门狗喂狗启动定时器和清理看门狗定时器;
cbai
发表于 2025-4-18 11:21:33
cbai 发表于 2025-4-17 17:53
第十二课学习心得:开头讲了复位系统的使用时机和作用,后面结合ai8051u单片机的硬件复位系统和软件复位系 ...
第十三课学习心得:当CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程就是中断;实现这种功能的部件就是中断系统,请示CPU中断的请求源称为中断源;单片机同时可以有多个中断源,cpu会根据中断源的优先级,优先处理最紧急事件的中断请求源(优先级最高的中断源),中断允许嵌套。用户可以用关闭/打开中断允许位来使CPU屏蔽或者响应相应的中断请求,根据原理图和芯片参考手册可以知道单片机有哪些中断源和对应的IO口,每一个中断源都有相应的中断号,声明终端服务需要带上关键字interrupt和对应的中断号。
cbai
发表于 2025-4-18 14:10:55
cbai 发表于 2025-4-18 11:21
第十三课学习心得:当CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处 ...
外部中断主逻辑
// 外部中断1初始化
void INT1_Init(void){
IT1 = 1; //下降沿中断
EX1 = 1; //使能中断允许位
EA = 1; //打开总中断允许位
}
void INT1_Isr(void) interrupt 2 {
P01 = !P01;
}
main.c中代码
void main(void) {
Sys_Init();
usb_init();
IE2 |= 0x80; // 允许USB中断
Timer0_Init();
INT1_Init();
Init_595();
EA = 1; // 开启中断总开关
P40 = 0;
// 2、P40低电平
// 3、P00 P01 P02 P03 P04 P05 P06 P07低电平
// WDT_CONTR = 0x24;
while(1) {
if (bUsbOutReady)
{
// USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done();
}
// Task_Pro_Handler_Callback(); //执行功能函数
// if(P33 != 0){
// USB_Reset_usb();
// IAP_CONTR |= 0x60; //P33按下复位到ISP
// }
// LED0灯每3秒亮灭
P00 = !P00;
Delay3000ms();
}
}