找回密码
 立即注册
楼主: 乐高

AI8051U教学视频课后小练 | 高质量打卡

[复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-2 09:55:44 | 显示全部楼层
第18集  串口高级应用  课后小练
本人愚钝,MODBUS-RTU 花了整整一个星期的时间才初步完成了此集课后小练,而且还是照搬了梁工的示例程序。
8H/8G/8C/8A系列4组串口同时通信的程序,Modbus 演示程序;可共享T2做波特率发生器 - 串行口,DMA支持的4组串口,RS232,RS485,Modbus, CRC16 国芯技术交流网站 - AI32位8051交流社区
本程序使用USB不间断下载接口,频率选择24M。实验箱J18跳线帽切换到C, D。串口助手更多设置选择“CRC-16 MODBUS协议,发送的数据包结尾增加校验数据”,串口发送接收都选择HEX模式。
J18串口切换跳帽.png 自动添加校验数据.png
  1. u8        MODBUS_RTU(void)
  2. {
  3.         u8        i,j,k;
  4.         u16        reg_addr;                                                                //寄存器地址
  5.         u8        reg_len;                                                                //写入寄存器个数
  6.         u16        crc;
  7.         if(RX1_Buffer[1] == 0x10)                                        //写多寄存器
  8.         {
  9.                 if(RX1_cnt < 9)                return 0x91;                //命令长度错误
  10.                 if((RX1_Buffer[4] != 0) || ((RX1_Buffer[5] *2) != RX1_Buffer[6]))        return 0x92;        //写入寄存器个数与字节数错误
  11.                 if((RX1_Buffer[5]==0) || (RX1_Buffer[5] > REG_LENGTH))        return 0x92;        //写入寄存器个数错误
  12.                 reg_addr = ((u16)RX1_Buffer[2] << 8) + RX1_Buffer[3];        //寄存器地址
  13.                 reg_len = RX1_Buffer[5];                                                                //写入寄存器个数
  14.                 if((reg_addr+(u16)RX1_Buffer[5]) > (REG_ADDRESS1+REG_LENGTH))        return 0x93;        //寄存器地址错误
  15.                 if(reg_addr < REG_ADDRESS1)                return 0x93;                        //寄存器地址错误
  16.                 if((reg_len*2+7) != RX1_cnt)        return 0x91;                        //命令长度错误
  17.                 j = reg_addr - REG_ADDRESS1;        //寄存器数据下标
  18.                 for(k=7, i=0; i<reg_len; i++,j++)
  19.                 {
  20.                         modbus_reg[j] = ((u16)RX1_Buffer[k] << 8) + RX1_Buffer[k+1];        //写入数据, 大端模式
  21.                         k += 2;
  22.                 }
  23.                 if(RX1_Buffer[0] != 0)        //非广播地址则应答
  24.                 {
  25.                         for(i=0; i<6; i++)        TX1_Buffer[i] = RX1_Buffer[i];        //要返回的应答
  26.                         crc = MODBUS_CRC16(TX1_Buffer, 6);
  27.                         TX1_Buffer[6] = (u8)crc;        //CRC是小端模式, 先发低字节,后发高字节。
  28.                         TX1_Buffer[7] = (u8)(crc>>8);
  29.                         B_TX1_Busy = 1;                //标志发送忙
  30.                         TX1_cnt    = 0;                //发送字节计数
  31.                         TX1_number = 8;                //要发送的字节数
  32.                         TI = 1;                                //启动发送
  33.                         
  34.                 }
  35.         }
  36.          else if(RX1_Buffer[1] == 0x06)        //写单寄存器
  37.         {
  38.                 if(RX1_cnt < 6)                return 0x91;                //命令长度错误
  39.                 //if((RX1_Buffer[4] != 0) || ((RX1_Buffer[5] *2) != RX1_Buffer[6]))        return 0x92;        //写入寄存器个数与字节数错误
  40.                 //if((RX1_Buffer[5]==0) || (RX1_Buffer[5] > REG_LENGTH))        return 0x92;        //写入寄存器个数错误
  41.                 reg_addr = ((u16)RX1_Buffer[2] << 8) + RX1_Buffer[3];        //寄存器地址
  42.                 //reg_len = RX1_Buffer[5];        //写入寄存器个数
  43.                 //if((reg_addr+(u16)RX1_Buffer[5]) > (REG_ADDRESS+REG_LENGTH))        return 0x93;        //寄存器地址错误
  44.                 //if(reg_addr != REG_ADDRESS)                return 0x93;        //寄存器地址错误
  45.                 if( RX1_cnt !=6)        return 0x91;        //命令长度错误
  46.                 j = reg_addr - REG_ADDRESS;        //寄存器数据下标
  47.                 modbus_reg[j] = ((u16)RX1_Buffer[4]<<8)+ RX1_Buffer[5];        //写入数据, 大端模式        
  48.                 num = modbus_reg[j];
  49.                 if(RX1_Buffer[0] != 0)        //非广播地址则应答
  50.                 {
  51.                         for(i=0; i<6; i++)        TX1_Buffer[i] = RX1_Buffer[i];        //要返回的应答
  52.                         crc = MODBUS_CRC16(TX1_Buffer, 6);
  53.                         TX1_Buffer[6] = (u8)crc;        //CRC是小端模式, 先发低字节,后发高字节。
  54.                         TX1_Buffer[7] = (u8)(crc>>8);
  55.                         B_TX1_Busy = 1;                //标志发送忙
  56.                         TX1_cnt    = 0;                //发送字节计数
  57.                         TX1_number = 8;                //要发送的字节数
  58.                         TI = 1;                                //启动发送
  59.                 }
  60.         }
  61.         
  62.         else if(RX1_Buffer[1] == 0x03)        //读多寄存器
  63.         {
  64.                 if(RX1_Buffer[0] != 0)        //非广播地址则应答
  65.                 {
  66.                         if(RX1_cnt != 6)                return 0x91;                //命令长度错误
  67.                         if(RX1_Buffer[4] != 0)        return 0x92;        //读出寄存器个数错误
  68.                         if((RX1_Buffer[5]==0) || (RX1_Buffer[5] > REG_LENGTH))        return 0x92;        //读出寄存器个数错误
  69.                         reg_addr = ((u16)RX1_Buffer[2] << 8) + RX1_Buffer[3];        //寄存器地址
  70.                         reg_len = RX1_Buffer[5];        //读出寄存器个数
  71.                         if((reg_addr+(u16)RX1_Buffer[5]) > (REG_ADDRESS1+REG_LENGTH))        return 0x93;        //寄存器地址错误
  72.                         //if(reg_addr < REG_ADDRESS1)                return 0x93;        //寄存器地址错误
  73.                         j = reg_addr - REG_ADDRESS1;        //寄存器数据下标
  74.                         TX1_Buffer[0] = SL_ADDR;        //站号地址
  75.                         TX1_Buffer[1] = 0x03;                //读功能码
  76.                         TX1_Buffer[2] = reg_len*2;        //返回字节数
  77.                         for(k=3, i=0; i<reg_len; i++,j++)
  78.                         {
  79.                                 TX1_Buffer[k++] = (u8)(modbus_reg[j] >> 8);        //数据为大端模式
  80.                                 TX1_Buffer[k++] = (u8)modbus_reg[j];
  81.                         }
  82.                         crc = MODBUS_CRC16(TX1_Buffer, k);
  83.                         TX1_Buffer[k++] = (u8)crc;        //CRC是小端模式, 先发低字节,后发高字节。
  84.                         TX1_Buffer[k++] = (u8)(crc>>8);
  85.                         B_TX1_Busy = 1;                //标志发送忙
  86.                         TX1_cnt    = 0;                //发送字节计数
  87.                         TX1_number = k;                //要发送的字节数
  88.                         TI = 1;                                //启动发送
  89.                 }
  90.         }
  91.         else        return 0x90;        //功能码错误
  92.         return 0;        //解析正确
  93. }
复制代码
示例代码.zip (131.49 KB, 下载次数: 40)   示例代码把数码管数据前的无效零位消隐了。
     









回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-8 14:22:48 | 显示全部楼层
第十九集  ADC     课间思考

按键显示.png
冲哥的意思是不是要求按下按键松开后,数码管显示键值保持不变,直到下一次按键按下?我的理解是这样的。
程序中增加KEY_Task(void)函数。
  1. void KEY_Task(void)
  2. {
  3.         u8 temp;       
  4.         temp =ADC_KEY_READ(ADC_Read(0));
  5.         if(temp)
  6.         {
  7.                 Key_Vol++;
  8.                 if( Key_Vol==5 )
  9.                 {
  10.                         key = temp;
  11.                 }
  12.         }
  13.                 else
  14.         {
  15.                 Key_Vol = 0;
  16.         }
  17. }       
复制代码
demo1.zip (229.74 KB, 下载次数: 29)

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-9 12:04:43 | 显示全部楼层
第十九集  ADC  课后小练

19集 简易电压表.png IMG_20250309_120340.jpg
定义P10引脚电压就是ADC按键端的电压为V_key,按键端ADC读取数值为value,MCU的电源电压为VCC(来自ME6231C33M5G稳压管,输出电压3.3V),MCU内部参考信号为BGV
反推ADC输入电压计算公式1.png 内部1.19V参考信号源.png MCU电源图.png
根据反推工作电压计算公式得到:VCC=4096*BGV/ADC_Read(15);VCC/4096=V_key/value;V_key=VCC*value/4096。
  1. u16 BGV_Read(void)
  2. {
  3.         u16 bgv = 0;
  4.         
  5.         
  6.         bgv = (((u16)CHIPID7) << 8) + (CHIPID8);        //获取内部参考信号源值
  7.         
  8.         return bgv;
  9. }
复制代码
  1. void KEY_Task(void)
  2. {
  3.         u8 temp;        
  4.         temp =ADC_KEY_READ(ADC_Read(0));
  5.         if(temp)
  6.         {
  7.                 Key_Vol++;
  8.                 if( Key_Vol==5 )
  9.                 {
  10.                         key = temp;
  11.                         value = ADC_Read(0);
  12.                 }
  13.         }
  14.                 else
  15.         {
  16.                 Key_Vol = 0;
  17.         }
  18.           VCC = 4096l*BGV_Read()/ADC_Read(15);
  19.           V_key = (u32)VCC*value/4096;
  20.           SEG_Show_U32( key,V_key);
  21.                 //SEG_Show_U32( key,VCC);
  22.                 //SEG_Show_U32( key,value);        
  23.                 //SEG_Show_U32( key,ADC_Read(15));        
  24.                 //SEG_Show_U32( key,BGV_Read());
  25. }        
复制代码

demo.zip (225.92 KB, 下载次数: 31)
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-9 20:25:36 | 显示全部楼层
第十九集  ADC  课后小练   程序修正

在上一个程序中,数码管显示的P10端口电压一直在跳动,排查发现是ADC_Read(15)的数值一直在变化,如果电源电压稳定,这应该是一个相对固定的值。
我考虑在程序开始的时候就把它读出来,采用连续读取20次求取平均值。修改后的结果数码管显示稳定。程序中4096后面是小写的“L”,不是数字“1”,用来转换后面的U16为U32长整型。
  1. /*  鉴于前所写程序中ADC_Read(15)的数值一直在跳动,而这个数值应该是一个相对固定值,
  2.         所以在程序运行的开始就把它读取出来,连续读取20次,求其平均值*/        
  3.         Delay10ms();
  4.         Delay10ms();
  5.         for( i=0;i<20;i++)                                                
  6.         {
  7.            BGV_ADC_Read += ADC_Read(15);
  8.            Delay10ms();
  9.         }
  10.         BGV_ADC_Read =BGV_ADC_Read/20;
  11.         VCC = 4096l*BGV_Read()/BGV_ADC_Read;
复制代码

demo2.zip (222.2 KB, 下载次数: 30)
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-12 16:04:42 | 显示全部楼层
第20集  ADC_NTC测温  课后小练

屏幕截图 2025-03-12 102857.png IMG_20250312_164149.jpg
NTC温度读取采用冲哥STC32G视频教程第19集NTC温度采集程序。 程序下载运行IRC频率24M,插上测温跳帽JUP2。
  1. void KEY_Task(void)
  2. {
  3.         u8 temp;
  4.         temp = ADC_KEY_READ(ADC_Read(0));        
  5.         if(temp)
  6.         {
  7.                 Key_Vol++;
  8.                 if( Key_Vol==5 )
  9.                 {
  10.                         key = temp;
  11.                         if(key==8)                        //“设置”按键
  12.                                 Set_t = 1;                //按下设置键,标志位Set_t置一
  13.                         if(key==16)                        //“确认退出”按键
  14.                                 Set_t = 0;
  15.                         if(Set_t)
  16.                         {
  17.                                 switch(key)     
  18.                                 {        
  19.                                         //温度增减按键
  20.                                         case 1:   if(baojing[0]<9)  baojing[0]++; break;      
  21.                                         case 2:   if(baojing[1]<9)  baojing[1]++; break;
  22.                                         case 3:   if(baojing[2]<9)  baojing[2]++; break;
  23.                                         case 4:   if(baojing[3]<9)  baojing[3]++; break;
  24.                                         case 9:   if(baojing[0]>0)  baojing[0]--; break;
  25.                                         case 10:  if(baojing[1]>0)  baojing[1]--; break;
  26.                                         case 11:  if(baojing[2]>0)  baojing[2]--; break;
  27.                                         case 12:  if(baojing[3]>0)  baojing[3]--; break;
  28.                                         default: break;
  29.                                 }                        
  30.                         }
  31.                 }
  32.         }
  33.                 else
  34.         {
  35.                 Key_Vol = 0;
  36.         }
  37.         BJ_wendu = baojing[3]*1000+baojing[2]*100+baojing[1]*10+baojing[0];   //报警温度放在数组baojing[]内
  38. }
  39. void NTC_Task(void)    //间隔500ms读取一次NTC值,兼作后四位数码管闪烁计时来源
  40. {
  41.         NTC_wendu = Temp_Cal( ADC_Read(3));    //NTC温度读取采用冲哥STC32G视频教程第19集NTC温度采集程序
  42.         VCC = 4096l*BGV_Read()/BGV_ADC_Read;   //MCU电压计算
  43.         SEG_Show(VCC,NTC_wendu);        //数码管显示,前四位显示电压,后四位显示NTC温度
  44.         if(Set_t)                                //按下设置键,标志位Set_t置一,后四位数码管闪烁
  45.                 blink = ~blink;
  46. }
复制代码
  1. void SEG_Task(void)
  2. {
  3.         if( Seg_no ==0 )                                                               
  4.         {
  5.                 Display_Seg( SEG_NUM[passward[0]]+0x80 , ~T_NUM[0]); //+0x80是为了添加小数点,编译会出现警告C188,可忽略
  6.         }
  7.         else if( Seg_no ==1 )                                                        
  8.         {
  9.                 Display_Seg( SEG_NUM[passward[1]] , ~T_NUM[1]);               
  10.         }        
  11.         else if( Seg_no ==2 )                                                        
  12.         {
  13.                 Display_Seg( SEG_NUM[passward[2]] , ~T_NUM[2]);               
  14.         }        
  15.         else if( Seg_no ==3 )                                                        
  16.         {
  17.                 Display_Seg( SEG_NUM[passward[3]] , ~T_NUM[3]);               
  18.         }
  19.         else if( Seg_no ==4 )
  20.         {
  21.                 if(passward[4]==0)
  22.                         Display_Seg( SEG_NUM[17] , ~T_NUM[0]);      //第一位如果是零,则消隐        
  23.                 else
  24.                         Display_Seg( SEG_NUM[passward[4]], ~T_NUM[4]);               
  25.         }        
  26.         else if( Seg_no ==5 )
  27.         {
  28.                 Display_Seg( SEG_NUM[passward[5]] , ~T_NUM[5]);               
  29.         }        
  30.         else if( Seg_no ==6 )
  31.         {
  32.                 if(Set_t )                                                          //按下设置键,后四位数码管闪烁
  33.                 {
  34.                         if(!blink)
  35.                                 Display_Seg( 0 , ~T_NUM[6]);
  36.                         else
  37.                                 Display_Seg( SEG_NUM[passward[6]]+0x80 , ~T_NUM[6]);
  38.                 }
  39.                 else if(NTC_wendu < BJ_wendu )                                                               
  40.                         Display_Seg( SEG_NUM[passward[6]]+0x80 , ~T_NUM[6]);
  41.                 else
  42.                         Display_Seg( SEG_NUM[passward[6]] , ~T_NUM[6]);
  43.         }
  44.         else if( Seg_no ==7 )
  45.         {
  46.                 Display_Seg( SEG_NUM[passward[7]] , ~T_NUM[7]);               
  47.         }        
  48.         else
  49.         {
  50.                
  51.         }
  52.         Seg_no ++;
  53.         if( Seg_no>7 )
  54.                 Seg_no=0;
  55. }
  56. void SEG_Show(u16 vol,u16 wendu)
  57. {
  58.         u8 i;
  59.         for(i=0;i<4;i++)
  60.         {
  61.                 if(Set_t)                                                       //按下设置键,后四位数码管闪烁
  62.                 {
  63.                         if(blink)
  64.                                 passward[7-i] = 17;
  65.                         else
  66.                                 passward[7-i] = baojing[i];
  67.                         
  68.                 }
  69.                 else
  70.                 {
  71.                         
  72.                         if(wendu > BJ_wendu)                      //如果温度高于预警温度值,则显示“----”
  73.                                 passward[7-i] = 16;
  74.                         else
  75.                         {
  76.                                 passward[7-i] = wendu%10;;
  77.                                 wendu /= 10;
  78.                         }
  79.                 }
  80.         }
  81.         for(i=4;i<8;i++)
  82.         {
  83.                 passward[7-i] = vol%10;
  84.                 vol /= 10;
  85.         }        
  86. }
复制代码


demo.zip (252.02 KB, 下载次数: 26)

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-16 22:28:50 | 显示全部楼层
第二十一集   Flash模拟EEPROM     课后小练
密码锁.png 下次下载不擦除用户EEPROM.png
虽然看上去只有几句要求,但做起来还是有点麻烦。按键又增加了一个,单独用矩阵键盘是不够用了。要用到ADC键盘,我把这两个键盘都用上了,可以同时工作,类似于笔记本电脑的大小键盘
下载程序的时候需要注意一点:不选择“下次下载用户程序时擦除用户EEPROM区”,不然保存在EEPROM 的数据,经过一次下载就没了,还不知道问题出在哪里。当然,如你需要,可以选择擦除。
  1. void main(void)
  2. {
  3.         Sys_init();                                                                                //系统初始化
  4.         usb_init();                                     //USB CDC 接口配置
  5.     IE2 |= 0x80;                                    //使能USB中断
  6.         Timer0_Init();                                                                        //定时器初始化
  7.         ADC_Init();
  8.     EA = 1;                                                                                        //IE |= 0X80;
  9.         WDT_CONTR = 0x25;                                                                //使能看门狗,溢出时间约1.05S
  10.         //while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置
  11.         //P40 = 0 ;
  12.         Parm_Init();
  13.         while(1)
  14.         {
  15.                
  16.                 if( blink_stop)                                                        //新密码输入完成,再次按下“#”键,这里是ADC键盘F号键
  17.                   if(ADC_KEY_READ(ADC_Read(0))==15)
  18.                         {  
  19.                                 admin = 0 ;blink_stop = 0;
  20.                                 EEPROM_SectorErase(E_ADDR);                //清空地址里的数据,擦除扇区
  21.                                 memcpy(dat,cod,8);
  22.                                 dat[8] = SYS_Run_times;
  23.                                 dat[9] = (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]);
  24.                                 EEPROM_write_n(E_ADDR,dat,10);
  25.                         
  26.                         }
  27.                
  28.                 Task_Pro_Handler_Callback();                                //执行功能函数
  29.                 WDT_CONTR = 0x35;                                                   //清看门狗
  30.         }
  31. }
复制代码
  1. u8 SYS_Run_times = 0;
  2. u8 dat[10];                        //0-7:8位密码   8:开机次数   9:校验码(累加校验)
  3. /*任务1:实现手册的EEPROM的基本操作,并用数码管显示当前的数值。并实现每次重新开机数值+1*/
  4. void Parm_Init(void)
  5. {
  6.         EEPROM_read_n( E_ADDR ,dat,10 );
  7.         if( dat[8] == 0xff )                        //如果读取到的数据是0xff,就是第一次上电,此时可以初始化
  8.         {
  9.                 EEPROM_SectorErase(E_ADDR);        //清空地址里的数据,擦除扇区
  10.                 memcpy(dat,cod2,8);  
  11.                 dat[8] = 1;
  12.                 dat[9] = (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]);
  13.                 EEPROM_write_n(E_ADDR,dat,10);
  14.                
  15.                 //SYS_Run_times = 1;
  16.         }
  17.         else                                                        //第二次及其之后的上电
  18.         {
  19.                 if( dat[9] == (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]) )        //校验通过,数据有效
  20.                 {
  21.                         SYS_Run_times = dat[8];
  22.                         SYS_Run_times +=1;
  23.                         memcpy(cod2,dat,8);                        
  24.                 }
  25.                 else
  26.                 {
  27.                         //如果数据校验出错,执行什么
  28.                 }
  29.         }
  30. }
复制代码
  1. sbit COL1 =        P0^0;                        //矩阵键盘的数据口端口定义
  2. sbit COL2 =        P0^1;
  3. sbit COL3 =        P0^2;                        
  4. sbit COL4 =        P0^3;
  5. sbit ROW1 =        P0^6;
  6. sbit ROW2 =        P0^7;
  7. //矩阵端口数据处理
  8. void Matrixkey_W(u8 m)            //向6个管脚写入数据
  9. {
  10.         COL1 = m & (0x01<<0);
  11.         COL2 = m & (0x01<<1);
  12.         COL3 = m & (0x01<<2);
  13.         COL4 = m & (0x01<<3);
  14.         ROW1 = m & (0x01<<4);
  15.         ROW2 = m & (0x01<<5);
  16. //        ROW3 = m & (0x01<<6);
  17. //        ROW4 = m & (0x01<<7);
  18. }
  19. u8 Matrixkey_R()                                 //从6个管脚读出数据
  20. {
  21.   u8 n =0;
  22.   // n  &= (u8)COL1;
  23.    if(COL1)                                                        
  24.          n |= 0x01;                                         
  25.    if(COL2)
  26.          n |= 0x02;
  27.    if(COL3)
  28.         n  |= 0x04;
  29.    if(COL4)
  30.          n |= 0x08;
  31.    if(ROW1)
  32.          n |= 0x10;
  33.    if(ROW2)
  34.          n |= 0x20;
  35. //   if(ROW3)
  36. //         n |= 0x40;
  37. //   if(ROW4)
  38. //         n |= 0x80;
  39.    return n;
  40. }
  41. void KEY_Task(void)                            //2*4矩阵键盘扫描
  42. {  
  43.         u8 key_temp;
  44.          u8 key1,key2;        
  45.          Matrixkey_W(0x30);
  46.         key1 = Matrixkey_R();
  47.         if(key1 != 0x30 )
  48.         {        
  49.                 key1 = Matrixkey_R()&0x30;                                    //确定哪一行的按键按下        
  50.                 Matrixkey_W(0x0f);
  51.                 key2 = Matrixkey_R();
  52.                 if(key2 != 0x0f)
  53.                         key2 = Matrixkey_R()&0x0f;                             //确定哪一列的按键按下
  54.                 key_temp=key1|key2;                                            //确定按键位置
  55.          }
  56.         else
  57.                 key_temp = key_no;
  58.         
  59.                 switch(key_temp)                                                //当确定按键按下后,列举所有的按键情况
  60.                 {   
  61.                          case 0x2e: key_value=0;break;
  62.                          case 0x2d: key_value=1;break;
  63.                          case 0x2b: key_value=2;break;
  64.                          case 0x27: key_value=3;break;
  65.                          case 0x1e: key_value=4;break;
  66.                          case 0x1d: key_value=5;break;
  67.                          case 0x1b: key_value=6;break;
  68.                          case 0x17: key_value=7;break;
  69.                          case 0xff: key_value=255;break;                        
  70.                  }        
  71. }
复制代码
  1. void PW_write_Task(void)
  2. {
  3.         u8 key ;
  4.         if(ADC_KEY_READ(ADC_Read(0))==15 & open)                //开锁后长按“#”号按键,这里设置ADC15号F按键为“#”号按键
  5.                 t_count++;
  6.                
  7.         if(t_count>300)                                                                        //长按“#”号按键3秒,进入管理员模式,admin置一
  8.         {
  9.                 admin = 1;
  10.                 memcpy(cod ,cod0,8);                             //将数组cod0的值复制给cod
  11.                 t_count = 0;
  12.         }
  13.         if(key_value<11)                                //只要矩阵键盘或者ADC键盘有按键按下,就把键值读入。
  14.                 key = key_value;                        //实现两组按键同时起作用
  15.         else
  16.                 key = ADC_KEY_READ(ADC_Read(0));
  17.         if( key< 11 )
  18.         {        
  19.                 Key_num ++ ;
  20.                
  21.                 if(Key_num ==6)
  22.                 {
  23.                          j++;
  24.                 if(open ==0 )
  25.                   {                                
  26.                          cod[j-1] = cod1[key];                                         //把收到的数字逐一存入cod[]中
  27.                         if(j==8)                                                                  //如果按了8个数字
  28.                          {
  29.                                 if(!memcmp(cod,cod2,8))                       //比较cod与cod2两数组是否相等,若相等显示“--OPEN--”,不等显示初始值“--------”
  30.                                         open = 1;                                
  31.                             else
  32.                                  {                                
  33.                                         memcpy(cod,cod0,8);                       //将初始值cod0复制给cod,回到起始状态
  34.                                         j = 0;
  35.                                  }
  36.                           }                        
  37.                    }
  38.                  if(admin)
  39.                  {
  40.                          cod[j-1] = cod1[key];                                          //进入管理员模式,把新的密码数字逐一存入cod[]中
  41.                                  if(j==8)                                                           //如果按了8个数字,停止闪烁,正常显示新的密码
  42.                          {
  43.                                  blink_stop = 1;
  44.                          }
  45.                  }
  46.                  if( key ==10)
  47.                         {
  48.                                 j = 0;
  49.                                 open =0;
  50.                         }
  51.                 if((open ==0)&&(j==0))
  52.                                 memcpy(cod,cod0,8);                               //将初始值cod0复制给cod,回到起始状态
  53.                 }               
  54.    }
  55.         else
  56.                 Key_num = 0;
  57. }
复制代码

21.EEPROM.zip (235.76 KB, 下载次数: 15)



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-17 08:27:16 | 显示全部楼层
第二十一集     Flash模拟EEPROM    课后小练补充
密码锁.png
这次我在程序中加入了查看开锁密码和开机次数的功能,把程序版本号改为了U 2.00。
设置的查看开锁密码的数组是“31415926”,查看开机次数的数组是“88889999”。
MCU运行频率24M。
  1. if(open ==0 )
  2.                   {                                
  3.                          cod[j-1] = cod1[key];                                         //把收到的数字逐一存入cod[]中
  4.                         if(j==8)                                                                  //如果按了8个数字
  5.                          {
  6.                                 if(!memcmp(cod,cod2,8))                       //比较cod与cod2两数组是否相等,若相等显示“--OPEN--”
  7.                                         open = 1;        
  8.                                 else if(!memcmp(cod,cod4,8))               //比较cod与cod4两数组是否相等,若相等则显示开锁密码
  9.                                         memcpy(cod,cod2,8);                       //将初始值cod2复制给cod
  10.                                 else if(!memcmp(cod,cod5,8))               //比较cod与cod5两数组是否相等,若相等则显示开机次数
  11.                                 {
  12.                                         cod[0] = 0;
  13.                                         cod[1] = 0;
  14.                                         cod[2] = 0;
  15.                                         cod[3] = 0;
  16.                                         cod[4] = 0;
  17.                                         cod[5] = SEG_NUM[SYS_Run_times/100];
  18.                                         cod[6] = SEG_NUM[SYS_Run_times%100/10];
  19.                                         cod[7] = SEG_NUM[SYS_Run_times%10];
  20.                                 }
  21.                             else                                                                 //不等显示初始值“--------”
  22.                                  {                                
  23.                                         memcpy(cod,cod0,8);                       //将初始值cod0复制给cod,回到起始状态
  24.                                         j = 0;
  25.                                  }
  26.                           }                        
  27.                    }
复制代码


21.EEPROM (U 2.00).zip (237.23 KB, 下载次数: 27)

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-19 11:06:18 | 显示全部楼层
触摸按键演示板

IMG_20250319_110739.jpg 触摸按键演示版.jpg
  1. void main(void)
  2. {
  3.         u8 i,j;
  4.         char xdata dat[50];
  5.         P_SW2 |=0x80;
  6.         UART1_config(1);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
  7. //1.需要用到的触摸引脚设置为高阻输入
  8.      P1M0 = 0x00; P1M1 = 0xf8;           //P1.3-P1.7
  9.         P3M0 = 0x00; P3M1 = 0x0c;                         //P3.2 、 P3.3,(P3.0 、 P3.1 作为串口1通讯口)
  10.                         
  11.    // P3M0 = 0x00; P3M1 = 0xfc;                  
  12.     P5M0 = 0x00; P5M1 = 0x10;               //P5.4
  13. //2.配置是否需要同时使用触摸LED指示和触摸检测
  14.         TSRT = 0;                                        //不需要使用LED
  15. //3.需要用到的触摸通道使能
  16.         TSCHEN1 = 0xfc;                                 
  17.         TSCHEN2 = 0x0c;
  18. //4.设置触摸按键的开关频率,参考电压和放电时间
  19.         TSCFG1 = (7<<4) + 6;   //工作频率设为最低,3 时钟为1000个系统时钟,6时钟为5000个系统时钟
  20.         TSCFG2 = 2;                       //参考电压为 5/8 AVCC
  21. //5. 开启触摸
  22.         TSCTRL = 0xa1 ;
  23. //6.开启触摸中断并保存数据
  24.         IE2 |= 0x80;
  25.         
  26.         EA = 1;        
  27. }
复制代码



触摸按键演示板.zip (100.59 KB, 下载次数: 30)

触摸按键演示板 (1).zip (101.26 KB, 下载次数: 21)   程序(1)修改了一下LED数组操作序号,这样更合理一些。
1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-27 16:22:36 | 显示全部楼层
第二十二集  比较器   课后小练

比较器密码锁.png 比较器电路部分.jpg
首先让我们对照电路图看程序,内部比较器的一端CMP-输入脚接内部1.19V参考电压;另一端CMP+接P4.6,P4.6接在USB+5V下面的可调电阻W1调节端。
CMPEXCFG |= 0x04;       //内部1.19V参考电压为CMP-输入脚   
CMPEXCFG &= ~0x03;   //P4.6为CMP+输入脚     
INVCMPO = 0;              //比较器正向输出  
CMPO_S = 0;                //选择P4.5作为比较器输出脚,P4.5在LCD12864模块接口的第四位。因为设定了比较器正向输出 ,所以当CMP+电压大于CMP-电压的时候,P4.5为高电平输出。
P42 = ~CMPRES;          //中断方式读取比较器比较结果,CMPRESS=0,LED11灭;CMPRESS=1,LED11亮。原程序是P42 = CMPRES; 为了节省断电后的电流,我把它取反了。
//在P4.5端口接上一个发光二极管,以及从实验箱上的LED11都可以观察比较器的输出结果,我把它们接成同相了。
比较器输出指示.jpg
在程序测试时,首先需要调节W1的阻值,使得P4.6端口的电压大于1.19V且不太多,这样可以使得掉电后,MCU有足够的时间去保存数据,而且不用在MCU两端加大容量电容。

  1. void PW_write_Task(void)
  2. {
  3.         u8 key ;
  4.         if(ADC_KEY_READ(ADC_Read(0))==15 & open)                //开锁后长按“#”号按键,这里设置ADC15号F按键为“#”号按键
  5.                 t_count++;
  6.                
  7.         if(t_count>300)                                                                        //长按“#”号按键3秒,进入管理员模式,admin置一
  8.         {
  9.                 admin = 1;
  10.                
  11.                 memcpy(cod ,cod0,8);                            //将数组cod0的值复制给cod
  12.                 t_count = 0;
  13.         }
  14.         if(key_value<11)                                //只要矩阵键盘或者ADC键盘有按键按下,就把键值读入。
  15.                 key = key_value;                        //实现两组按键同时起作用
  16.         else
  17.                 key = ADC_KEY_READ(ADC_Read(0));
  18.         if( key< 11 )
  19.         {        
  20.                 Key_num ++ ;
  21.                
  22.                 if(Key_num ==6)
  23.                 {
  24.                          j++;
  25.                 if(open ==0 )
  26.                   {                                
  27.                          cod[j-1] = cod1[key];                                         //把收到的数字逐一存入cod[]中
  28.                         if(j==8)                                                                  //如果按了8个数字
  29.                          {
  30.                                 if(!memcmp(cod,cod2,8))                       //比较cod与cod2两数组是否相等,若相等显示“--OPEN--”,不等显示初始值“--------”
  31.                                 {
  32.                                         open = 1;
  33.                                         open_t++;                                                 //开门次数加一
  34.                                 }
  35.                                 else if(!memcmp(cod,cod4,8))               //比较cod与cod4两数组是否相等,若相等则显示开锁密码
  36.                                         memcpy(cod,cod2,8);                       //将初始值cod2复制给cod
  37.                                 else if(!memcmp(cod,cod5,8))               //比较cod与cod5两数组是否相等,若相等则显示开机次数
  38.                                 {
  39.                                         /*依次显示3位错输次数,3位开门次数,2位进管理员模式次数*/
  40.                                         cod[0] = SEG_NUM[err_t/100];
  41.                                         cod[1] = SEG_NUM[err_t%100/10];
  42.                                         cod[2] = SEG_NUM[err_t%10];
  43.                                         cod[3] = SEG_NUM[open_t/100];
  44.                                         cod[4] = SEG_NUM[open_t%100/10];
  45.                                         cod[5] = SEG_NUM[open_t%10];
  46.                                         cod[6] = SEG_NUM[adm_t%100/10];
  47.                                         cod[7] = SEG_NUM[adm_t%10];
  48.                                 }
  49.                             else                                       
  50.                                  {                                
  51.                                         err_t++;                                                 //输入错误,错输次数加一
  52.                                         memcpy(cod,cod0,8);                       //将初始值cod0复制给cod,回到起始状态
  53.                                         j = 0;
  54.                                  }
  55.                           }                        
  56.                    }
  57.                  if(admin)
  58.                  {
  59.                          cod[j-1] = cod1[key];                                          //进入管理员模式,把新的密码数字逐一存入cod[]中
  60.                                  if(j==8)                                                           //如果按了8个数字,停止闪烁,正常显示新的密码
  61.                          {
  62.                                  blink_stop = 1;
  63.                                  adm_t++;                                                        //完成一次修改密码,进管理员次数加一
  64.                          }
  65.                  }
  66.                  if( key ==10)
  67.                         {
  68.                                 j = 0;
  69.                                 open =0;
  70.                                 if(admin)
  71.                                 {
  72.                                         adm_t++;                                                //中途退出,进管理员次数也加一次
  73.                                         admin = 0;
  74.                                 }
  75.                         }
  76.                 if((open ==0)&&(j==0))
  77.                                 memcpy(cod,cod0,8);                               //将初始值cod0复制给cod,回到起始状态
  78.                 }               
  79.    }
  80.         else
  81.                 Key_num = 0;
  82.                 dat[8] = err_t;
  83.                 dat[9] = open_t;
  84.                 dat[10] = adm_t;
  85.                 dat[11] = (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]+dat[9]+dat[10]);
  86. }
复制代码



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:110
  • 最近打卡:2025-05-02 16:57:27
已绑定手机

1

主题

60

回帖

283

积分

中级会员

积分
283
发表于 2025-3-27 16:57:44 | 显示全部楼层
第二十二集    比较器  课后小练(续)

比较器密码锁.png
我在二十一集程序的基础上稍作了一些增删。仍然采用输入31415926来读出开门密码,输入88889999来显示(一次8位同时显示)1、密码输入错误次数(前3位),2、开门次数(中3位),3、进入管理员模式次数(后2位)。如下图:密码输入错误6次,开门5次,进入管理员模式3次。
IMG_20250327_164420.jpg
  1. //========================================================================
  2. // 函数: void CMP_config(void)
  3. // 描述: 比较器初始化函数。
  4. // 参数: 无.
  5. // 返回: 无.
  6. // 版本: V1.0, 2020-6-10
  7. //========================================================================
  8. void CMP_config(void)
  9. {
  10.     CMPEXCFG = 0x00;
  11. //  CMPEXCFG |= 0x40;       //比较器DC迟滞输入选择,0:0mV; 0x40:10mV; 0x80:20mV; 0xc0:30mV
  12. //  CMPEXCFG &= ~0x04;      //P4.4为CMP-输入脚
  13.     CMPEXCFG |= 0x04;       //内部1.19V参考电压为CMP-输入脚
  14.     CMPEXCFG &= ~0x03;      //P4.6为CMP+输入脚
  15. //  CMPEXCFG |= 0x01;       //P5.0为CMP+输入脚
  16. //  CMPEXCFG |= 0x02;       //P5.1为CMP+输入脚
  17. //  CMPEXCFG |= 0x03;       //ADC输入脚为CMP+输入脚
  18.     CMPCR2 = 0x00;
  19.     INVCMPO = 0;            //比较器正向输出
  20. //  INVCMPO = 1;            //比较器反向输出
  21.     DISFLT = 0;             //使能0.1us滤波
  22. //  DISFLT = 1;             //禁止0.1us滤波
  23. //  CMPCR2 &= ~0x3f;        //比较器结果直接输出
  24.     CMPCR2 |= 0x10;         //比较器结果经过16个去抖时钟后输出
  25.     CMPCR1 = 0x00;
  26. //  PIE = 0;                //禁止比较器上升沿中断
  27.     PIE = 1;                //使能比较器上升沿中断
  28. //  NIE = 0;                //禁止比较器下降沿中断
  29.     NIE = 1;                //使能比较器下降沿中断
  30. //  CMPOE = 0;              //禁止比较器输出
  31.     CMPOE = 1;              //使能比较器输出
  32.     CMPO_S = 0;             //选择P4.5作为比较器输出脚
  33. //  CMPO_S = 1;             //选择P4.1作为比较器输出脚
  34.     CMPEN = 1;              //使能比较器模块
  35. }
复制代码
  1. u8 dat[12];                        //0-7:8位开门密码   8:错输次数   9:开门次数  10:进管理员模式次数   11:校验码(累加校验)
  2. void Parm_Init(void)
  3. {
  4.         EEPROM_read_n( E_ADDR ,dat,12 );
  5.         if( dat[8] == 0xff )                        //如果读取到的数据是0xff,就是第一次上电,此时可以初始化
  6.         {
  7.                 memcpy(dat,cod2,8);
  8.                
  9.         }
  10.         else
  11.         {
  12.          if( dat[11] == (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]+dat[9]+dat[10]) )        //校验通过,数据有效
  13.          {
  14.                 EEPROM_SectorErase(E_ADDR);        //清空地址里的数据,擦除扇区
  15.                 memcpy(cod2,dat,8);
  16.                 err_t = dat[8];
  17.                 open_t = dat[9];
  18.                 adm_t = dat[10];
  19.          }
  20.         }
  21. }
复制代码
  1. /******************* 比较器中断函数 ********************/
  2. void CMP_Isr() interrupt 21
  3. {
  4.    // u8  i;
  5.     CMPIF = 0;          //清中断标志
  6.     P42 = ~CMPRES;       //中断方式读取比较器比较结果,CMPRESS=0,LED11灭;CMPRESS=1,LED11亮。
  7.     if(CMPRES)
  8.     {
  9.         if(LowVolFlag)
  10.         {
  11.             LowVolFlag = 0;             //清除低电压标志
  12.                         memcpy(cod,cod0,8);                 //点亮数码管
  13. //            if(Test_cnt != Temp_cnt)
  14. //            {
  15. //                EEPROM_read_n(E_ADDR,tmp,2);        //读出2字节
  16. //                Test_cnt = ((u16)tmp[0] << 8) + tmp[1]; //秒计数
  17. //                if(Test_cnt > 10000)    Test_cnt = 0;   //秒计数范围为0~10000
  18. //                Temp_cnt = Test_cnt;
  19. //            }
  20.         }
  21.     }
  22.     else
  23.     {
  24.                 //Display_Seg(0,0);
  25.                 memcpy(cod,cod6,8);           //熄灭数码管
  26.         if(!LowVolFlag)
  27.         {
  28.             LowVolFlag = 1;           //设置低电压标志                                       
  29. //                        dat[8] = err_t;
  30. //                        dat[9] = open_t;
  31. //                        dat[10] = adm_t;
  32. //                        dat[11] = (u8)(dat[0]+dat[1]+dat[2]+dat[3]+dat[4]+dat[5]+dat[6]+dat[7]+dat[8]+dat[9]+dat[10]);
  33.                         EEPROM_write_n(E_ADDR,dat,12);
  34. //            if(Test_cnt != Temp_cnt)
  35. //            {
  36. //                Temp_cnt = Test_cnt;
  37. //                EEPROM_SectorErase(EE_ADDRESS); //擦除扇区
  38. //                tmp[0] = (u8)(Temp_cnt >> 8);
  39. //                tmp[1] = (u8)Temp_cnt;
  40. //                EEPROM_write_n(EE_ADDRESS,tmp,2);
  41. //            }
  42.         }
  43.     }
  44. }
复制代码

22.比较器.zip (404.72 KB, 下载次数: 26)

回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-3 12:54 , Processed in 3.837397 second(s), 113 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表