一,
语音模块:数据包发送
刷卡模块:数据包接收
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;