学习笔记-沁恒第四讲-米醋

发布于:2025-02-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

一,

语音模块:数据包发送

刷卡模块:数据包接收

AS608:数据包发送+接收

二,第三讲文件夹改成第四讲,工程也改成第四讲

三,目前在内存里面。保存新值,掉电会丢失

u8 password[6]={1,2,3,4,5,6};        //开锁密码

u8 password_cmd[6]={2,7,7,5,1,6};    //管理员密码

四,BY8301-16P

Flash里面存储语音,通过IO口/串口,播放哪一条

Micro USB的接口

五,芯片好坏验证:音频生成,导入

生成想要的语音

喇叭接上能响就行,正负接线影响不大,磁场往外振动,和往里面吸,能响就行

语音模块接上电脑后

导入BY8301语音更新工具(语音模块连接电脑自动打开)

(更新进去:把正在播放的去掉)

(更新慢原因:串口读写,擦写flash,校验)

IO口1到7对应0001.mp3~0007.mp3

给IO口低电平使能,验证模块好坏

六,语音模块BY8301-16P(不是语音芯片)有很多功能(音量加减,上一曲),要发数据包,来控制

操作码:比如0x31:调整音量,0x41:选择曲目

校验码:发送数据过程中,可能丢包(复杂的电磁环境中,数据可能跳变:一个0跳成1)

和校验

发两个数 和 跳变的话,和就不一定对了

11 12 23

数据跳变了就返回一个0什么的,要求重新发

参数1,2(十六进制):两个字节,0~65535

例如:

选择播放曲目指令为 7E 05 41 00 01 45 EF

长度 05 是这样得到:就是“05”,“41”,“00”,“01”,“45”5 位数长度;

校验码 45 是这样得到:

首先打开计算器选择程序员模式;

然后选择 16 进制、双字;

最后点击进行计算(除去起始码和结束码) 05 Xor 41 Xor 00 Xor 01 = 45

  • 串口接Rx 好坏验证:能不能接收数据包

共地

电脑给芯片发

7E 05 41 00 01 45 EF播放0001

7E 05 41 00 02 46 EF播放0002

八,必须要有中断函数

单片机操作数据包

串口:发送数据/接收数据 都会进中断

中断服务函数里判断 是发送中断 还是 接收中断

//串口3中断(发送中断+接收中断)

void USART3_IRQHandler(void)

{

    u8 temp;

    if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)

    {

        temp=USART_ReceiveData(USART3);

    }

    USART_ClearITPendingBit(USART3, USART_IT_RXNE);

}

没有的话:发送完数据,触发了中断,找不到中断函数,指向不确定的地方(代码只能执行一次)

所以,触发中断后,判断一下发送中断 还是 接收中断:只用发送,也得有

九,硬件层,驱动层,应用层

十,串口发送完的标志位USART_FLAG_TC用于查询方式串口 发数据

void audio_init()

{

    Usart3_Init();

}

void audio_play(u8 num)//查询写

{

   u8 string[]={0x7e,0x05,0x41,0x00,num,0x05^0x41^0x00^num,0xef};

   u8 i;

   for(i=0;i<7;i++)

   {

       USART_SendData(USART3,string[i]);

       while(  USART_GetFlagStatus(USART3, USART_FLAG_TC)==0 );

//单片机96M的频率,串口慢,需要给单片机减速,但用delay太低级

   }

}

十一,单片机 通过串口 向语音模块 发送一串数据,第一个数据语音模块没接收到

发送0x7e,0x05,0x41,0x00,0x05,0x05^0x41^0x00^0x05,0xef

收到   0x05,0x41,0x00,0x05,0x05^0x41^0x00^0x05,0xef

原因:

上电初始化时,

发送标志位USART_FLAG_TC就是1,

第一次发送的时候,显示已经发送完成了,跳过第一次发送,跳过while

(跳过while但跳不过Delay,但Delay太低级)

解决方法1:

void Usart3_Init()的最后

USART_ClearFlag(USART3,USART_FLAG_TC);//清空串口3的发送标志位

解决方法2:

考试的时候 只知道 第一个数据丢了,但不知道为什么:

那就第一个数据发两次

十二,CH32不支持位寻址,不能用bit

十三,设置输入正确密码开锁,5秒后自动关锁

十四,key_temp最后一位得不是0~9

u8 password[6]={1,2,3,4,5,6};        //开锁密码

u8 password_cmd[6]={2,7,7,5,1,6};    //管理员密码

u8 key_temp[7]={10,10,10,10,10,10};  //按键暂存区

u8 key_index;                        //按键索引,已经输入的密码位数

u8 key_index_old;                    //同上

假如开锁密码是268390

输入了   26839后最后一位默认是0,没输入门就开了

所以u8 key_temp[7]={10,10,10,10,10,10};

按键是0~9

十五,密码输入完开门后,得清空密码

#include "string.h" //数组函数相关头文件

void *  memset (void *, int, size_t);

数组,要复制数组的数字,复制几位

void key_clear()//键盘暂存区清空函数

{

    memset(key_temp,10,6);

    key_index=0;

}

  • 按键回退

if(key_index)

{

key_index--;

key_temp[key_index]=10;

}

按键清空key_clear();

十七,密码输入错误3次,锁定

u8 password_error;                   //密码错误次数

u16 time15s=15;                      //密码错误锁定倒计时15秒

/*按键处理函数*/

void key_proc()

{

  if(password_error==3)return;锁定

  switch(key_down)

  {

     case 16://确认按键

if( string_chek(key_temp,password,6) )密码输入正确

  {

   lock_flag=0;//开门

   show_flag=1;//显示汉字们已开启

   audio_play(3);//播放语音

   key_clear();//清空状态

   password_error=0;

  }

else 密码输入错误

  {

   audio_play(4);//播放语音

   key_clear();

   if(++password_error==3)

   {

      audio_play(5);

      show_flag=2;

   }

  }

      break;//确认按键的Brak

  }

}

/*系统计时定时器3中断服务函数*/

void TIM3_IRQHandler(void)//每1毫秒

{

    if(TIM_GetITStatus(TIM3, TIM_IT_Update)!=RESET)

    {

if(password_error==3)//如果密码错误3次

{

      if(--time15s==0)//15秒倒计时

      {

       time15s=15000;

       password_error=0;//密码错误次数归零

       show_flag=0;

      }

}

    }

    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

}

十八,发生变化的时候,更新,(如果一直更新,会闪烁)

u8 show_flag;                        //显示汉字标志位

u8 show_flag_old;                    //显示汉字标志位

按键里面操作/其他地方操作,改flag就行

{“门”,0x04,0x00,0xC8,0x3F,0x08,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x20,0x02,0x28,0x02,0x10},/*"门",0*/

{“以”,0x00,0x08,0x20,0x08,0x44,0x08,0x84,0x08,0x84,0x08,0x04,0x08,0x04,0x08,0x04,0x04,0x04,0x04,0x04,0x04,0x24,0x02,0x14,0x0A,0x0C,0x11,0x84,0x20,0x40,0x40,0x20,0x40},/*"以",1*/

{“开”,0x00,0x00,0xFE,0x3F,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0xFF,0x7F,0x10,0x04,0x10,0x04,0x10,0x04,0x10,0x04,0x08,0x04,0x08,0x04,0x04,0x04,0x02,0x04},/*"开",2*/

{“启”,0x80,0x00,0x00,0x01,0xF8,0x3F,0x08,0x20,0x08,0x20,0x08,0x20,0xF8,0x3F,0x08,0x00,0x08,0x00,0x08,0x00,0xE8,0x3F,0x24,0x20,0x24,0x20,0x22,0x20,0xE1,0x3F,0x20,0x20},/*"启",3*/

{“,”,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x0C,0x00,0x08,0x00,0x04,0x00,0x00,0x00},/*",",4*/

{“欢”,0x00,0x01,0x00,0x01,0x3F,0x01,0x20,0x3F,0xA0,0x20,0x92,0x10,0x54,0x02,0x28,0x02,0x08,0x02,0x14,0x05,0x24,0x05,0xA2,0x08,0x81,0x08,0x40,0x10,0x20,0x20,0x10,0x40},/*"欢",5*/

{“迎”,0x00,0x00,0x04,0x01,0xC8,0x3C,0x48,0x24,0x40,0x24,0x40,0x24,0x4F,0x24,0x48,0x24,0x48,0x24,0x48,0x2D,0xC8,0x14,0x48,0x04,0x08,0x04,0x14,0x04,0xE2,0x7F,0x00,0x00},/*"迎",6*/

{“回”,0x00,0x00,0xFC,0x1F,0x04,0x10,0x04,0x10,0xE4,0x13,0x24,0x12,0x24,0x12,0x24,0x12,0x24,0x12,0x24,0x12,0xE4,0x13,0x04,0x10,0x04,0x10,0xFC,0x1F,0x04,0x10,0x00,0x00},/*"回",7*/

{“家”,0x40,0x00,0x80,0x00,0xFE,0x7F,0x02,0x40,0x01,0x20,0xFE,0x3F,0x40,0x00,0xB0,0x10,0x8E,0x09,0x40,0x05,0x30,0x03,0x8E,0x05,0x60,0x19,0x18,0x61,0x47,0x01,0x80,0x00},/*"家",8*/

/*屏幕处理函数*/

void lcd_proc()

{

/*黄色密码输入区,*显示*/

//输入密码,更新key_index

    if(key_index!=key_index_old)   //如果暂存区位数发生了变化

    {

         u8 i=key_index;

         LCD_Fill(16,45,112,66,YELLOW);//填充黄色背景

         if(key_index==7)key_index=6;  //限位防止数组越界

         while(i--)//循环显示*

         {

             if(i<6)

             LCD_ShowChar(20+16*i,45,'*',RED,YELLOW,16,0);

         }

         key_index_old=key_index; //更新索引值

    }

   /*屏幕顶部白色字符显示区*/

   if(show_flag!=show_flag_old)显示模式变化时,刷新白色区域

   {

       LCD_Fill(0,0,128,32,WHITE);//填充白色闪的原因,所以放里面

      show_flag_old=show_flag;

   }

     switch(show_flag)这一部分实时刷新

        {

        case 0://门已上锁

        lcd_show_chinese(0,0,"门已上锁,请输入密码",RED,WHITE,16,0);

        break;

        case 1:

        lcd_show_chinese(0,0,"门已开启,欢迎回家",RED,WHITE,16,0);

x,y,字符串,字色RED,背景色WHITE,字号16,非叠加模式

        break;

        case 2://锁定倒计时

        LCD_ShowIntNum(50, 16,time15s,2, RED,WHITE,16);

x,y,数字,显示两位,字色RED,背景色WHITE,字号16

        break;

        }

}

  • 确认按键要复用

u8 mode;

//模式,0主页,1修改密码没有解锁管理员权限,2修改密码模式解锁了管理员权限

//3录入卡片,没有解锁管理员权限  4录入卡片,解锁了管理员权限

u8 password[6]={1,2,3,4,5,6};        //开锁密码

u8 password_cmd[6]={2,7,7,5,1,6};    //管理员密码

u8 key_temp[7]={10,10,10,10,10,10};  //按键暂存区

u8 key_index;                        //按键索引,已经输入的密码位数

u8 key_index_old;                    //同上

二十,主页模式下,如果密码输入一半,突然要改密码(进入修改密码模式),得清空一次暂存区

二十一,RFID卡坏不坏验证:串口调试

喇叭:单片机告诉喇叭,你播放哪个(单片机Tx接喇叭Rx)

RFID:刷卡,RFID告诉单片机卡号是什么(RFID的Tx接单片机Rx)

卡层:

把卡放上去,模块会通过串口发卡号出来

单片机层面:

怎么改卡里面的密码,怎么读写卡(把卡里面的扇区改掉),储存新的卡,解锁匹配

接线:

RFID的Tx接Rx

VIN接VCC

GD接GND

二十二,RFID卡要求

XH36050-A1读写器规格书:工作电压:直流 3.3V-5V ,(最大输入电压不超过 6V)

高频读写器系列使用手册V1.3

波特率可以猜:RFID上刷卡,

每次显示的数据不一样,

明显是一个数据包,却每次刷卡只显示一个两个。

那就是波特率有问题

二十三,数据手册阅读方法

两张卡片分别刷一次,

显示04 0C 02 30 00 04 00 6E B9 92 D6 52

显示04 0C 02 30 00 04 00 C3 70 A2 00 10

明显看出前面是包头,后面是卡号

数据手册里搜一下0x04

二十四,RFID与喇叭共用串口3是不行的:因为两者需要的波特率不一样

串口3接收功能:RFID(RFID的Tx接单片机PB11)

串口3发送功能:喇叭

USART2的Rx:PA3

Tx:PA2

二十五,调试:串口1打印

不管校验,就当是对的

#include "debug.h"  //必要系统头文件

USART_Printf_Init(115200);//串口1初始化

串口号选择WCH-Link 板载的串口

打开串口

1,验证能不能打印printf(“hellow\r\n”);(不加换行,不能正常打印(可能通过捕捉\r\n把前面的打印))

2,验证能不能进中断

二十六,录入卡片,刷录入的卡,能直接开锁

u8 mode;

//模式,0主页,1修改密码没有解锁管理员权限,2修改密码模式解锁了管理员权限

//3录入卡片,没有解锁管理员权限  4录入卡片,解锁了管理员权限

u8 rfid_index;                       //刷卡模块接收索引

u8 rfid_temp[4]={0};                 //卡号暂存数组

u8 rfid[4][4]={0}; //卡片存储二维数组,前一个中括号是第几张能开锁的卡

u8 rfid_password_index;              //卡片存储索引第几张卡

二十七,RFID卡,USART2中断,捕获并解析数据包

void USART2_IRQHandler(void)

{

    u8 temp;

    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)

    {

        temp=USART_ReceiveData(USART2);

        switch(rfid_index)

                {

                    case 0:

                        if(temp==0x04)rfid_index++;

printf(1\r\n);

                    break;

                    case 1:

                        if(temp==0x0c)rfid_index++;

                        else rfid_index=0;

printf(2\r\n);

                    break;

                    case 2:

                        if(temp==0x02)rfid_index++;

                        else rfid_index=0;

printf(3\r\n);这个printf的过程,会干扰接收,能显示1,2,就把前两个printf删了

                    break;

                    case 3:

                        if(temp==0x30)rfid_index++;

                        else rfid_index=0;

                    break;

                    case 4:

                        if(temp==0x00)rfid_index++;

                        else rfid_index=0;

                    break;

                    case 5:

                        if(temp==0x04)rfid_index++;

                        else rfid_index=0;

                    break;

                    case 6:

                        if(temp==0x00)rfid_index++;

                        else rfid_index=0;

                    break;

                    case 7:

                        rfid_temp[0]=temp;

                        rfid_index++;

                    break;

                    case 8:

                        rfid_temp[1]=temp;

                        rfid_index++;

                    break;

                    case 9:

                        rfid_temp[2]=temp;

                        rfid_index++;

                    break;

                    case 10:

                        rfid_temp[3]=temp;

                        rfid_index=0;

接收完数据后

                        if(mode==0)主页模式下

                        {

                            if( rfid_chek() )如果卡片ID正确(四张卡片符合谁)

                            {

                                audio_play(11);

                                lock_flag=0;

                            }

                            else如果卡片ID错误

                            {

                                audio_play(12);

                            }

                        }

                        if(mode==4)录入卡片,解锁了管理员权限

                        {

                          

将新卡的ID录入二维数组rfid[4][4]

string_copy(rfid_temp,rfid[rfid_password_index],4);

                            audio_play(15);

录入后,回主页模式

                            mode=0;

不自增的话:

rfid_chek()for (i=0; i<rfid_password_index; ++i)循环不到刚录入的

而且无法录入更多

                            rfid_password_index++;

                        }

                    break;

                }

    }

    USART_ClearITPendingBit(USART2, USART_IT_RXNE);

}

检查录入的卡片 与 正在刷的卡片,ID相同不

u8 rfid_chek()

{

    u8 i;

    for (i=0; i<rfid_password_index; ++i)

    {

       if( string_chek(rfid_temp,rfid[i],4) )return 1;

    }

    return 0;

}

二十七,二维数组和一维数组,在内存中的存储是一样的

寻址方式不同,本质是一串地址

一维数组,直接偏移

二维数组,偏移量乘起来

num[0][6]=7

1 2 3

4 5 6

7 8 9

if( string_chek(rfid_temp,rfid[i],4) )return 1;