国二,使用AI8051U芯片为主控获得全国大学生电子设计竞赛国家二等奖
2025年电赛I题非接触智能控制盘国二辽宁机电职业技术学院===非接触智能控制盘:
我们在硬件上面遇到了各种难题,需要自制硬件超声波传感器,设计电机调压调速控制电路。最后完成基本要求和发挥部分。
我们一开始使用运放构成运算放大器和比较器的方法处理超声波信号,发现效果并不理想后来采取CS100A为核心搭建的
超声波收发电路外围电路简单可靠性高,工作时序由图1超声波距离测量时序图所示:
离计算公式:file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml5468/wps22.jpg
file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml5468/wps23.jpg为超声波在空气中的传播速度(340m/s),代入得简化公式:
理论误差:主要来自声速随温度的变化,修正公式为v=331.5+0.607T(T 为环境温度℃)。
测量误差: Ai8051U的定时器精度为25ns,对应距离分辨率 0.00017cm,满足 ±1cm 的设计要求。
风扇电压控制电路采用 "PWM - 低通滤波 - 线性稳压" 三级调节架构,下图PWM电压调节器的电路模型。
单片机输出占空比为D,频率1kHz的 PWM 信号,经RC低通滤波器转换为直流电压。该电压通过 LM358组成的缓冲器和电压PI调节器,
控制LM317 的输出电压:file:///C:/Users/ADMINI~1/AppData/Local/Temp/ksohtml5468/wps26.jpg
其中 k 为运放增益(实测 1.259),通过校准得到 PWM 占空比与输出电压的线性关系为UD = 0.1259x + 0.0064 (R2=0.9998)
通过 11 组实测数据验证,该模型在 23.25%-87.5% 占空比范围内的拟合误差≤0.05V,满足电压调节精度要求。
PWM 占空比与风扇电压控制电路输出的测试数据
占空比%23.252424.7537.543.755062.575808587.5
电压UD(V)2.8953.0193.1344.745.536.317.889.4510.0810.7110.98
最终版风扇电压控制电路图如下所示:
本系统采用 Ai8051U单片机,这款单片机不需要外部晶振和外部复位便可正常工作比传统8051约快70倍以上;支持在系统编程方式(ISP)更新用户应用程序
无需专用编程器;支持12位高精度15通道的模数转换,速度最快能达到800K。主控单片机最小系统电路图如下。
四个漫反射光电开关(U12-U14)采用并联设计,额外的LED3~6提供良好的状况反馈。当手掌遮挡光电开关时,输出端从高电平跳变为低电平
单片机在中断服务程序中记录触发时间和通道号,通过时序分析识别手势轨迹。电开关电路图如下所示。
程序基于Keil C51编译器开发环境,程序流程图如下:
主要程序模块包括:
1.主程序模块:采用前后台架构,主循环负责状态刷新和显示,中断服务程序处理实时事件(代码片段如下):
void main(void)
{
unsigned char i;
unsigned char a;
unsigned char temp;
unsigned int max_val = 0;
unsigned int min_val = 0;
P_SW2 = 0x80;
System_Init();
EA = 1;
Delay500ms();
EEPROM_read_n(0x0000,(unsigned char*)f3_data1,64);
Bee = 1;
Delay500ms();
Delay500ms();
Bee = 0;
Delay500ms();
Delay500ms();
Bee = 1;
Delay500ms();
Delay500ms();
Bee = 0;
while (1)
{
TCON &= 0xfc;
Check();
max_val = csb_dat;
min_val = csb_dat;
for (i = 1; i < 5; i++)
{
if (csb_dat > max_val)
{
max_val = csb_dat;// 更新最大值
}
if (csb_dat < min_val)
{
min_val = csb_dat;// 更新最小值
}
}
if((max_val - min_val < biaozhunzhi) && (Timer1_All < 350))
{
juliwending = 10;
P37 = 0;
if(Timer1_All != 0)
wendingjuli = Timer1_All;
}
else
{
juliwending = 0;
P37 = 1;
}
temp = 0;
while(P27 == 0)
{
Delay10ms();
temp++;
if(temp>50)
{
for(a=0;a<16;a++)
{
f3_data1 = 0;
f3_data1 = 0;
EEPROM_SectorErase(0x0000);
EEPROM_write_n(0x0000,(unsigned char*)f3_data1,64);
}
}
}
2.手势识别模块:通过中断记录开关触发时序,采用数组存储最近 5 次触发事件,通过模式匹配识别手势:
unsigned char key(void)
{
if((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON))//四个当中有一个被按下
{
if(K_S1 == K_ON)//按下的是按钮1
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}//1s内不释放就报错
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))//1s内释放,1s内不按下一个就报错
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s4;
}
}
}
return zero;
}
else if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s4;
}
}
}
else return zero;
}
else if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s4;
}
}
}
else return zero;
}
else if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s4;
}
}
}
else return zero;
}
return zero;
}
else return zero;
}
3.PWM 调节模块:采用定时器 0 输出 10kHzPWM,通过查表法实现电压快速调节:
#define PINLV5000 //修改频率PWMA
#define System 40000000//晶振,下载时选择的频率
#define PINLVB1000 //修改频率PWMB#define PWMB5_0 0x00 //P:P0.1
#define PWMB5_1 0x01 //P:P1.1
#define PWMB5_2 0x02 //P:P2.1
#define PWMB5_3 0x03 //P:P5.0#define ENO5P 0x01
#define ENO6P 0x04
#define ENO7P 0x10
#define ENO8P 0x40unsigned int PWM_PERIODB = System/PINLVB;unsigned int PWM5_Duty;//百分比变void UpdatePwm(void)//放在while循环里面,如果有断点自动下载,放在if的前面
{
// PWMA_CCR1H = (u8)(PWM1_Duty >> 8);
// PWMA_CCR1L = (u8)(PWM1_Duty);
// PWMA_CCR2H = (u8)(PWM2_Duty >> 8);
// PWMA_CCR2L = (u8)(PWM2_Duty);
// PWMA_CCR3H = (u8)(PWM3_Duty >> 8);
// PWMA_CCR3L = (u8)(PWM3_Duty);
// PWMA_CCR4H = (u8)(PWM4_Duty >> 8);
// PWMA_CCR4L = (u8)(PWM4_Duty);
PWMB_CCR5H = (u8)(PWM5_Duty >> 8);
PWMB_CCR5L = (u8)(PWM5_Duty);
// PWMB_CCR6H = (u8)(PWM6_Duty >> 8);
// PWMB_CCR6L = (u8)(PWM6_Duty);
// PWMB_CCR7H = (u8)(PWM7_Duty >> 8);
// PWMB_CCR7L = (u8)(PWM7_Duty);
// PWMB_CCR8H = (u8)(PWM8_Duty >> 8);
// PWMB_CCR8L = (u8)(PWM8_Duty);
}void AI8051_PWM(void)
{
WTST = 0; //将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展访问寄存器
CKCON = 0; //提高访问XRAM的速度
//=======占空比配置==================================================================================================//
// PWM1_Duty = 4000;//PWM_PERIOD为1,占空比为50%就把PWM_PERIOD x 1/2
// PWM2_Duty = 4000;
// PWM3_Duty = 4000;
// PWM4_Duty = 4000;
PWM5_Duty = 20000;//PWM_PERIOD为1,占空比为50%就把PWM_PERIOD x 1/2
// PWM6_Duty = 4000;
// PWM7_Duty = 4000;
// PWM8_Duty = 4000;
//=======通道模式配置==================================================================================================//
// PWMA_CCER1 = 0x00; //写CCMR之前要清零CCER
// PWMA_CCER2 = 0x00;
PWMB_CCER1 = 0x00; //写CCMR之前要清零CCER
PWMB_CCER2 = 0x00;
// PWMA_CCMR1 = 0x60; //通道模式配置
PWMB_CCMR1 = 0x60; //通道模式配置
// PWMA_CCMR2 = 0x60;
PWMB_CCMR2 = 0x60;
// PWMA_CCMR3 = 0x60;
PWMB_CCMR3 = 0x60;
// PWMA_CCMR4 = 0x60;
PWMB_CCMR4 = 0x60;
// PWMA_CCER1 = 0x55; //配置通道输出和极性
// PWMA_CCER2 = 0x55;
PWMB_CCER1 = 0x55; //写CCMR之前要清零CCER
PWMB_CCER2 = 0x55;
// PWMA_CCMR1 |= 0x08;//开启预装载
// PWMA_CCMR2 |= 0x08;
// PWMA_CCMR3 |= 0x08;
// PWMA_CCMR4 |= 0x08;
PWMB_CCMR1 |= 0x08;//开启预装载
PWMB_CCMR2 |= 0x08;
PWMB_CCMR3 |= 0x08;
PWMB_CCMR4 |= 0x08;
// PWMB_PSCRH = 0x00; //分频
// PWMB_PSCRL = 99;
//=======周期配置==================================================================================================//
// PWMA_ARRH = (u8)(PWM_PERIOD >> 8); //周期时间配置,在开头define配置
// PWMA_ARRL = (u8)PWM_PERIOD;
PWMB_ARRH = (u8)(PWM_PERIODB >> 8); //周期时间配置,在开头define配置
PWMB_ARRL = (u8)PWM_PERIODB;
//=======使能配置==================================================================================================//
// PWMA_ENO |= ENO1P; //使能输出
// PWMA_ENO |= ENO1N; //使能输出
// PWMA_ENO |= ENO2P; //使能输出
// PWMA_ENO |= ENO2N; //使能输出
// PWMA_ENO |= ENO3P; //使能输出
// PWMA_ENO |= ENO3N; //使能输出
// PWMA_ENO |= ENO4P; //使能输出
// PWMA_ENO |= ENO4N; //使能输出
// PWMA_ENO |= ENO4N; //使能输出
PWMB_ENO |= ENO5P; //使能输出
// PWMB_ENO |= ENO6P; //使能输出
// PWMB_ENO |= ENO7P; //使能输出
// PWMB_ENO |= ENO8P; //使能输出
//=======通道选择配置==================================================================================================//
// PWMA_PS = 0x00; //通道模式
PWMB_PS = 0x00; //通道模式
PWMB_PS |= PWMB5_0;
// PWMB_PS |= PWMB6_3;
// PWMB_PS |= PWMB7_2;
// PWMB_PS |= PWMB8_2;
//PWMA_PS |= PWM2_1;
//PWMA_PS |= PWM3_1;
// PWMA_PS |= PWM4_2;
//=======使能主输出配置==================================================================================================//
// PWMA_BKR = 0x80; //使能主输出
PWMB_BKR = 0x80; //使能主输出
// PWMA_CR1 |= 0x81; //使能预装载
PWMB_CR1 |= 0x81; //使能预装载
}
4.超声波测距模块:超声波测距
void Timer1_Isr(void) interrupt 3
{
Timer1_Count++;
}
void Timer1_Init(void)//100微秒@40.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x80;
TL1 = 0x00; //设置定时初始值
TH1 = 0x00; //设置定时初始值
TF1 = 0; //清除TF1标志
// TR1 = 0; //定时器1关闭计时
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}
void INT1_Isr(void) interrupt 2
{
u32 temp;
// u8 i;
TR1 = 0; //定时器1关闭计时
EX1 = 0; //关闭INT1中断
((u8*)&temp) = ((u8*)&Timer1_Count);
((u8*)&temp) = ((u8*)&Timer1_Count);
((u8*)&temp) = TH1;
((u8*)&temp) = TL1;
temp = temp * 0.0042875; // * 25 * 343000 / 1000000000 / 2
//--- 移动数据 -----------
if(temp != 0)
{
if(++csb_dat_x >= 5)csb_dat_x = 0;
csb_dat = temp; //存入新数据
Timer1_All = temp ;/// 5;
}
TL1 = 0x00; //设置定时初始值
TH1 = 0x00; //设置定时初始值
Timer1_Count = 0;
TR1 = 1;
EX1 = 1;
P36 = !P36;
}
演示视频如下
934
933
935
在设计中,力求硬件线路简单,充分发挥软件编程灵活的特点,来满足系统设计要求。系统以Ai8051U单片机为控制核心、自制信号稳定的超声波测距模块、优良的PWM调压电路,以及合理高效的单片机程序设计,使得我们顺利控制完成题目要求。经过几天努力奋战,从开始准备到第一时间接到题目,一直都全身心地投入比赛之中。
感谢STC提供的AI8051U实验箱和芯片,感谢学校老师后勤工作支持,同时也感谢大赛组委会给了我们这次重要的机会锻炼自己。
尊敬的各位评审老师:
您好!感谢您在百忙之中审阅我们的参赛作品。我们是来自辽宁机电职业技术学院的参赛团队,荣获2025年全国大学生电子设计竞赛I题“非接触智能控制盘”项目国家级二等奖。在此,我们谨向组委会提交本项目的奖金申领申请,并简要汇报我们在该项目中的技术实现与成果。
在本次比赛中,我们围绕“非接触智能控制盘”这一主题,完成了从系统设计、硬件搭建到软件算法开发的全过程。项目旨在实现对设备的非接触式智能控制,通过超声波传感器检测物体位置信息,并结合电机驱动模块实现精准控制。
在硬件设计方面,我们面临诸多挑战。初期采用运放构成运算放大器和比较器处理超声波信号,但由于噪声干扰大、稳定性差,导致测量精度不理想。经过反复调试与优化,我们最终选用CS100A作为核心器件,构建了结构简单、可靠性高的超声波收发电路。该电路具有良好的抗干扰能力,能够稳定地采集目标距离信息。
在系统时序控制方面,我们设计了如图1所示的超声波距离测量时序图,确保各模块协同工作,提高整体系统的响应速度与准确性。基于采集到的距离数据,我们建立了相应的数学模型,用于计算目标与控制盘之间的相对位置关系。公式如下:
$$ d = \frac{v \cdot t}{2} $$
其中,$ d $ 表示目标与控制盘之间的距离,$ v $ 为超声波在空气中的传播速度(约340 m/s),$ t $ 为超声波发射与接收之间的时间差。
在控制部分,我们设计了电机调压调速控制电路,实现了对电机转速的精确调节。通过PWM(脉冲宽度调制)技术,我们能够根据距离变化动态调整电机输出功率,从而实现对控制对象的精准操控。
在软件算法层面,我们编写了基于单片机的控制程序,实现了对超声波信号的实时处理、数据解析与电机控制逻辑的集成。整个系统具备良好的可扩展性与适应性,能够满足不同场景下的应用需求。
通过本次比赛,我们不仅提升了自身的工程实践能力,也加深了对嵌入式系统、传感器技术和自动控制原理的理解。我们深知,此次获奖不仅是对我们努力的认可,更是对未来继续深入研究的激励。
因此,我们诚挚申请2025年电赛奖金,并希望得到组委会的审核与支持。我们将继续秉持严谨求实的态度,在今后的学习与实践中不断提升自我,为我国电子信息技术的发展贡献力量。
此致
敬礼!
辽宁机电职业技术学院
2025年电赛I题“非接触智能控制盘”参赛团队
2025年X月X日
[本文内容由国芯人工智能辅助生成,仅供参考] 主程序代码如下
void main(void)
{
unsigned char i;
unsigned char a;
unsigned char temp;
unsigned int max_val = 0;
unsigned int min_val = 0;
P_SW2 = 0x80;
System_Init();
EA = 1;
Delay500ms();
EEPROM_read_n(0x0000,(unsigned char*)f3_data1,64);
Bee = 1;
Delay500ms();
Delay500ms();
Bee = 0;
Delay500ms();
Delay500ms();
Bee = 1;
Delay500ms();
Delay500ms();
Bee = 0;
while (1)
{
TCON &= 0xfc;
Check();
max_val = csb_dat;
min_val = csb_dat;
for (i = 1; i < 5; i++)
{
if (csb_dat > max_val)
{
max_val = csb_dat;// 更新最大值
}
if (csb_dat < min_val)
{
min_val = csb_dat;// 更新最小值
}
}
if((max_val - min_val < biaozhunzhi) && (Timer1_All < 350))
{
juliwending = 10;
P37 = 0;
if(Timer1_All != 0)
wendingjuli = Timer1_All;
}
else
{
juliwending = 0;
P37 = 1;
}
temp = 0;
while(P27 == 0)
{
Delay10ms();
temp++;
if(temp>50)
{
for(a=0;a<16;a++)
{
f3_data1 = 0;
f3_data1 = 0;
EEPROM_SectorErase(0x0000);
EEPROM_write_n(0x0000,(unsigned char*)f3_data1,64);
}
}
}
2.手势识别模块:通过中断记录开关触发时序,采用数组存储最近 5 次触发事件,通过模式匹配识别手势:
unsigned char key(void)
{
if((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON))//四个当中有一个被按下
{
if(K_S1 == K_ON)//按下的是按钮1
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}//1s内不释放就报错
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))//1s内释放,1s内不按下一个就报错
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s4;
}
}
}
return zero;
}
else if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s4;
}
}
}
else return zero;
}
else if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s4;
}
}
}
else return zero;
}
else if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s4;
}
}
}
else return zero;
}
return zero;
}
else return zero;
}
3.PWM 调节模块:采用定时器 0 输出 10kHzPWM,通过查表法实现电压快速调节:
#define PINLV5000 //修改频率PWMA
#define System 40000000//晶振,下载时选择的频率
#define PINLVB1000 //修改频率PWMB#define PWMB5_0 0x00 //P:P0.1
#define PWMB5_1 0x01 //P:P1.1
#define PWMB5_2 0x02 //P:P2.1
#define PWMB5_3 0x03 //P:P5.0#define ENO5P 0x01
#define ENO6P 0x04
#define ENO7P 0x10
#define ENO8P 0x40unsigned int PWM_PERIODB = System/PINLVB;unsigned int PWM5_Duty;//百分比变void UpdatePwm(void)//放在while循环里面,如果有断点自动下载,放在if的前面
{
// PWMA_CCR1H = (u8)(PWM1_Duty >> 8);
// PWMA_CCR1L = (u8)(PWM1_Duty);
// PWMA_CCR2H = (u8)(PWM2_Duty >> 8);
// PWMA_CCR2L = (u8)(PWM2_Duty);
// PWMA_CCR3H = (u8)(PWM3_Duty >> 8);
// PWMA_CCR3L = (u8)(PWM3_Duty);
// PWMA_CCR4H = (u8)(PWM4_Duty >> 8);
// PWMA_CCR4L = (u8)(PWM4_Duty);
PWMB_CCR5H = (u8)(PWM5_Duty >> 8);
PWMB_CCR5L = (u8)(PWM5_Duty);
// PWMB_CCR6H = (u8)(PWM6_Duty >> 8);
// PWMB_CCR6L = (u8)(PWM6_Duty);
// PWMB_CCR7H = (u8)(PWM7_Duty >> 8);
// PWMB_CCR7L = (u8)(PWM7_Duty);
// PWMB_CCR8H = (u8)(PWM8_Duty >> 8);
// PWMB_CCR8L = (u8)(PWM8_Duty);
}void AI8051_PWM(void)
{
WTST = 0; //将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展访问寄存器
CKCON = 0; //提高访问XRAM的速度
//=======占空比配置==================================================================================================//
// PWM1_Duty = 4000;//PWM_PERIOD为1,占空比为50%就把PWM_PERIOD x 1/2
// PWM2_Duty = 4000;
// PWM3_Duty = 4000;
// PWM4_Duty = 4000;
PWM5_Duty = 20000;//PWM_PERIOD为1,占空比为50%就把PWM_PERIOD x 1/2
// PWM6_Duty = 4000;
// PWM7_Duty = 4000;
// PWM8_Duty = 4000;
//=======通道模式配置==================================================================================================//
// PWMA_CCER1 = 0x00; //写CCMR之前要清零CCER
// PWMA_CCER2 = 0x00;
PWMB_CCER1 = 0x00; //写CCMR之前要清零CCER
PWMB_CCER2 = 0x00;
// PWMA_CCMR1 = 0x60; //通道模式配置
PWMB_CCMR1 = 0x60; //通道模式配置
// PWMA_CCMR2 = 0x60;
PWMB_CCMR2 = 0x60;
// PWMA_CCMR3 = 0x60;
PWMB_CCMR3 = 0x60;
// PWMA_CCMR4 = 0x60;
PWMB_CCMR4 = 0x60;
// PWMA_CCER1 = 0x55; //配置通道输出和极性
// PWMA_CCER2 = 0x55;
PWMB_CCER1 = 0x55; //写CCMR之前要清零CCER
PWMB_CCER2 = 0x55;
// PWMA_CCMR1 |= 0x08;//开启预装载
// PWMA_CCMR2 |= 0x08;
// PWMA_CCMR3 |= 0x08;
// PWMA_CCMR4 |= 0x08;
PWMB_CCMR1 |= 0x08;//开启预装载
PWMB_CCMR2 |= 0x08;
PWMB_CCMR3 |= 0x08;
PWMB_CCMR4 |= 0x08;
// PWMB_PSCRH = 0x00; //分频
// PWMB_PSCRL = 99;
//=======周期配置==================================================================================================//
// PWMA_ARRH = (u8)(PWM_PERIOD >> 8); //周期时间配置,在开头define配置
// PWMA_ARRL = (u8)PWM_PERIOD;
PWMB_ARRH = (u8)(PWM_PERIODB >> 8); //周期时间配置,在开头define配置
PWMB_ARRL = (u8)PWM_PERIODB;
//=======使能配置==================================================================================================//
// PWMA_ENO |= ENO1P; //使能输出
// PWMA_ENO |= ENO1N; //使能输出
// PWMA_ENO |= ENO2P; //使能输出
// PWMA_ENO |= ENO2N; //使能输出
// PWMA_ENO |= ENO3P; //使能输出
// PWMA_ENO |= ENO3N; //使能输出
// PWMA_ENO |= ENO4P; //使能输出
// PWMA_ENO |= ENO4N; //使能输出
// PWMA_ENO |= ENO4N; //使能输出
PWMB_ENO |= ENO5P; //使能输出
// PWMB_ENO |= ENO6P; //使能输出
// PWMB_ENO |= ENO7P; //使能输出
// PWMB_ENO |= ENO8P; //使能输出
//=======通道选择配置==================================================================================================//
// PWMA_PS = 0x00; //通道模式
PWMB_PS = 0x00; //通道模式
PWMB_PS |= PWMB5_0;
// PWMB_PS |= PWMB6_3;
// PWMB_PS |= PWMB7_2;
// PWMB_PS |= PWMB8_2;
//PWMA_PS |= PWM2_1;
//PWMA_PS |= PWM3_1;
// PWMA_PS |= PWM4_2;
//=======使能主输出配置==================================================================================================//
// PWMA_BKR = 0x80; //使能主输出
PWMB_BKR = 0x80; //使能主输出
// PWMA_CR1 |= 0x81; //使能预装载
PWMB_CR1 |= 0x81; //使能预装载
}
4.超声波测距模块:超声波测距
void Timer1_Isr(void) interrupt 3
{
Timer1_Count++;
}
void Timer1_Init(void)//100微秒@40.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x80;
TL1 = 0x00; //设置定时初始值
TH1 = 0x00; //设置定时初始值
TF1 = 0; //清除TF1标志
// TR1 = 0; //定时器1关闭计时
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
}
void INT1_Isr(void) interrupt 2
{
u32 temp;
// u8 i;
TR1 = 0; //定时器1关闭计时
EX1 = 0; //关闭INT1中断
((u8*)&temp) = ((u8*)&Timer1_Count);
((u8*)&temp) = ((u8*)&Timer1_Count);
((u8*)&temp) = TH1;
((u8*)&temp) = TL1;
temp = temp * 0.0042875; // * 25 * 343000 / 1000000000 / 2
//--- 移动数据 -----------
if(temp != 0)
{
if(++csb_dat_x >= 5)csb_dat_x = 0;
csb_dat = temp; //存入新数据
Timer1_All = temp ;/// 5;
}
TL1 = 0x00; //设置定时初始值
TH1 = 0x00; //设置定时初始值
Timer1_Count = 0;
TR1 = 1;
EX1 = 1;
P36 = !P36;
} chb2005 发表于 2025-11-5 14:27
主程序代码如下
void main(void)
串口通信配置文件
#include <AI8051U.H>
//============================================================================================================================串口2
unsigned char xdata Uart_Send_2; //发送缓冲区,最多50个字节
unsigned char xdata Uart_Receive_2; //接收缓冲区,最多50个字节
unsigned char Index_Send_2 = 0; //全局变量,用于中断发送缓冲区里的索引
unsigned char Index_Receive_2 = 0; //全局变量,用于中断接收缓冲区里的索引
unsigned char Best_Num_2; //全局变量,最大数,也就是发送字节的数量
bit Check_Busy_2 = 0; //发送的标志位,很重要
/*===============================================================================
//函数:Uart_Send_String
//描述:用于向串口发送字符串,原理是通过中断发送
//参数:*Data,用“ ”存放字符串。Best,字符串总数
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void Uart_Send_String_2(const unsigned char *Data,unsigned char Best)
{
unsigned char i;
if(Check_Busy_2) //检测到忙就退出
{
return;
}
for(i = 0; i < Best ;i++)
{
Uart_Send_2 = Data; //Data赋值给缓冲区数组
}
Best_Num_2 = Best; //把字符长度传递到全局变量
Index_Send_2 = 0; //索引清零
S2BUF = Uart_Send_2; //把Data赋值给缓冲区数组的第一个赋给对应串口的数据寄存器
Check_Busy_2 = 1; //置忙位
}
/*===============================
//函数:Uart2_Isr
//描述:中断函数,当软件给S2BUF赋值一次后便会进入中断
//参数:无
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void Uart2_Isr(void) interrupt 8
{
if (S2CON & 0x02) //检测串口2发送中断
{
S2CON &= ~0x02; //清除串口2发送中断请求位
if(Check_Busy_2)
{
Index_Send_2++;
if(Index_Send_2 < Best_Num_2)
{
S2BUF = Uart_Send_2;
}
else
{
Check_Busy_2 = 0;
}
}
}
if (S2CON & 0x01) //检测串口2接收中断
{
S2CON &= ~0x01; //清除串口2接收中断请求位
Uart_Receive_2 = S2BUF;
}
}
/*===============================
//函数:Uart2_Init
//描述:串口通信2初始化,40MHZ,9600波特率
//参数:无
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void Uart2_Init(void) //9600bps@40.000MHz
{
P_SW2 |= 0x01; //UART2/USART2: RxD2(P4.2), TxD2(P4.3)
S2CON = 0x50; //8位数据,可变波特率
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0xEE; //设置定时初始值
T2H = 0xFB; //设置定时初始值
AUXR |= 0x10; //定时器2开始计时
IE2 |= 0x01; //使能串口2中断
Index_Receive_2 = 0;
}
unsigned char String; //存放long类型转换为字符串的数据
unsigned char Hex_To_DecString_Flag = 0; //位数标志位
/*===============================
//函数:Hex_To_DecString
//描述:用于把long长整型变量转换成十进制字符串
//参数:Data,要转换的变量
//返回:全局变量String[]数组,存储十进制字符串;全局变量Hex_To_DecString_Flag存储位数,代表字符串长度
//版本:2025.7 徐靖昀
=================================*/
void Hex_To_DecString(unsigned long Data)
{
unsigned long Change = Data; //Change算位数,Data转换成数组
unsigned char Count = 0; //Count存储位数
if (Change == 0)
{
String = '0'; // 处理数值为0的特殊情况
Hex_To_DecString_Flag = 1;
return;
}
while(Change > 0)
{
Change /= 10;
Count++;
}
Hex_To_DecString_Flag = Count;
while(Data>0)
{
String[--Count] = (Data%10)+'0';
Data /= 10;
}
}
/*===============================
//函数:Num_All
//描述:用于发送自定义数组,为字符串1,字符串1长度,变量1,字符串2,字符串2长度,变量2,字符串3,字符串3长度
//参数:*Data_1,*Data_2,*Data_3,Num_1,Num_2,Num_3,NUM,DEC
//返回:全局变量数组All_Num[],存储整合后的数组;Send_Math,整个字符串的长度
//版本:2025.7 徐靖昀
=================================*/
unsigned char All_Num;
unsigned char Send_Math = 0;
void Num_All(unsigned char *Data_1 ,unsigned char Num_1 ,unsigned long NUM ,unsigned char *Data_2 ,unsigned char Num_2 ,unsigned long DEC ,unsigned char *Last,unsigned char Num_3)
{
unsigned char i_1;
unsigned char j_5;
unsigned char B_2;
unsigned char Z_4;
unsigned char X_3;
unsigned char NUM_Long;
unsigned char DEC_Long;
for(i_1 = 0;i_1 < Num_1;i_1++) //第一个文本
{
All_Num = Data_1;
}
Hex_To_DecString(NUM);
NUM_Long = Hex_To_DecString_Flag;
for(B_2 = 0;B_2 < NUM_Long;B_2++) //第一个变量
{
All_Num = String;
}
for(X_3 = 0;X_3 < Num_2;X_3++) //第二个文本
{
All_Num = Data_2;
}
Hex_To_DecString(DEC);
DEC_Long = Hex_To_DecString_Flag;
for(Z_4 = 0;Z_4 < DEC_Long;Z_4++) //第二个变量
{
All_Num = String;
}
for(j_5 = 0;j_5 < Num_3;j_5++) //第三个文本
{
All_Num[(Num_1+Num_2+DEC_Long+NUM_Long+j_5)] = Last;
}
Send_Math = Num_1 + Num_2 + Num_3 + NUM_Long + DEC_Long; //总的变量的长度
}
/*===============================
//函数:String_All
//描述:用于发送自定义的字符串.格式为:字符串1,字符串1长度,变量1,字符串2,字符串2长度,字符串3,字符串3长度,字符串4,字符串4长度
//参数:*Data_1,Num_1,NUM,*Data_2,Num_2,*Data_3,Num_3,*Data_4,Num_4
//返回:全局变量数组Show_String_ALL[]用于存储整个数组;变量String_Math,为整个数组的长度
//版本:2025.7 徐靖昀
=================================*/
unsigned char Show_String_ALL;
unsigned char String_Math = 0;
void String_All(unsigned char *Data_1 ,unsigned char Num_1 ,unsigned long NUM ,unsigned char *Data_2 ,unsigned char Num_2 ,unsigned char *Data_3,unsigned char Num_3,unsigned char *Last,unsigned char Num_4)
{
unsigned char O;
unsigned char T;
unsigned char T_2;
unsigned char F;
unsigned char F_2;
unsigned char NUM_Long;
for(O = 0;O < Num_1;O++) //第一个文本
{
Show_String_ALL = Data_1;
}
Hex_To_DecString(NUM);
NUM_Long = Hex_To_DecString_Flag;
for(T = 0;T < NUM_Long;T++) //第一个变量
{
Show_String_ALL = String;
}
for(T_2 = 0;T_2 < Num_2;T_2++) //第二个文本
{
Show_String_ALL = Data_2;
}
for(F = 0;F < Num_3;F++) //第二个文本
{
Show_String_ALL = Data_3;
}
for(F_2 = 0;F_2 < Num_4;F_2++) //第三个文本
{
Show_String_ALL[(Num_1+Num_2+Num_3+NUM_Long+F_2)] = Last;
}
String_Math = Num_1 + Num_2 + Num_3 + NUM_Long + Num_4; //总的变量的长度
}
/*===============================
//函数:LCD_Send_Number
//描述:向串口屏发送数字变量
//参数:Digit:序号选择,Change数字
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void LCD_Send_Number(unsigned char Digit,unsigned long Change)
{
Num_All("n",1,Digit,".val=",5,Change,"\xff\xff\xff",3);
Uart_Send_String_2(All_Num,Send_Math);
}
/*===============================
//函数:LCD_Send_String
//描述:向串口屏发送文本
//参数:Digit:序号;*Change:要发送的文本;L_ong:文本长度
//注意:中文字符串长度为文字数量的二倍!!!如LCD_Send_String(0,“单片机”,6)
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void LCD_Send_String(unsigned char Digit,unsigned char *Change,unsigned char L_ong)
{
String_All("t",1,Digit,".txt=\"",6,Change,L_ong,"\"\xff\xff\xff",4);
Uart_Send_String_2(Show_String_ALL,String_Math);
}
/*===============================
//函数:LCD_Send_Float
//描述:向串口屏发送数字变量
//参数:Digit:序号选择,Change数字
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void LCD_Send_Float(unsigned char Digit,unsigned long Change)
{
Num_All("x",1,Digit,".val=",5,Change,"\xff\xff\xff",3);
Uart_Send_String_2(All_Num,Send_Math);
}
/*===============================
//函数:LCD_Send_Colour
//描述:向串口屏发送数字变量
//参数:Digit:序号选择,Change数字
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void LCD_Send_Clolur(unsigned char Digit,unsigned long Change)
{
// Num_All("r",1,Digit,".bco=",5,Change,"\xff\xff\xff",3);
// Uart_Send_String_2(All_Num,Send_Math);
Num_All("r",1,Digit,".pco=",5,Change,"\xff\xff\xff",3);
Uart_Send_String_2(All_Num,Send_Math);
}
//绿色为2032
//红色为63488
/*===============================================================================
//函数:UartTask_2
//描述:设置接收数值时单片机对应的操作
//参数:无
//返回:无
//版本:2025.7 徐靖昀
=================================*/
void UartTask_2(void) //接收串口要执行的任务
{
if( Index_Receive_2 >= 2 ) //判断最少N个字节
{
if((Uart_Receive_2 == 'N')) //末尾判断
{
if((Uart_Receive_2 == 'N') )
{
P0 = 0x00;
Uart_Send_String_2 ("n0.val=15\xFF\xFF\xFF",12);
Index_Receive_2 = 0;
}
}
else if((Uart_Receive_2 == '\n') && (Uart_Receive_2 == '\r')&&(Uart_Receive_2 == 'x') && (Uart_Receive_2 == 'j') && (Uart_Receive_2 == 'y'))
{
P0 = 0xFF;
Uart_Send_String_2("Yes\r\n",5);
Index_Receive_2 = 0;
}
}
} chb2005 发表于 2025-11-5 14:37
串口通信配置文件
#include
手势判断
#include "AI8051U.H"
#include "intrins.h"
#include "shoushi20250730.H"
unsigned int k_cs;//按键释放超时计数减一变量
void Delay_key(void) //@40.000MHz1ms
{
unsigned long edata i;
_nop_();
_nop_();
_nop_();
i = 9998UL;
while (i) i--;
}
void Delay10ms(void) //@40.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 99998UL;
while (i) i--;
}
unsigned char key(void)
{
if((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)) //四个当中有一个被按下
{
if(K_S1 == K_ON) //按下的是按钮1
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
} //1s内不释放就报错
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON))) //1s内释放,1s内不按下一个就报错
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s1s4;
}
}
}
return zero;
}
else if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s2s4;
}
}
}
else return zero;
}
else if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s3s4;
}
}
}
else return zero;
}
else if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
k_cs = k_sts_d;
while(!((K_S1 == K_ON)||(K_S2 == K_ON)||(K_S3 == K_ON)||(K_S4 == K_ON)))
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
if(K_S1 == K_ON)
{
Delay10ms();
if(K_S1 == K_ON)
{
k_cs = k_off_d;
while(K_S1 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s1;
}
}
if(K_S2 == K_ON)
{
Delay10ms();
if(K_S2 == K_ON)
{
k_cs = k_off_d;
while(K_S2 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s2;
}
}
if(K_S3 == K_ON)
{
Delay10ms();
if(K_S3 == K_ON)
{
k_cs = k_off_d;
while(K_S3 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s3;
}
}
if(K_S4 == K_ON)
{
Delay10ms();
if(K_S4 == K_ON)
{
k_cs = k_off_d;
while(K_S4 == K_ON)
{
Delay_key();
k_cs--;
if(k_cs == 0)return K_Err;
}
return s4s4;
}
}
}
else return zero;
}
return zero;
}
else return zero;
} chb2005 发表于 2025-11-5 14:41
手势判断
#include "AI8051U.H"
#include "intrins.h"
掉电存储
#include <AI8051U.H>
#include "intrins.h"
#include "def.h"
#include "AI8051U_EEPROM.h"
//========================================================================
// 函数: void ISP_Disable(void)
// 描述: 禁止访问ISP/IAP.
// 参数: non.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void DisableEEPROM(void)
{
IAP_CONTR = 0; //禁止IAP操作
IAP_CMD = 0; //去除IAP命令
IAP_TRIG= 0; //防止IAP命令误触发
IAP_ADDRE = 0xff; //将地址设置到非 IAP 区域
IAP_ADDRH = 0xff; //将地址设置到非 IAP 区域
IAP_ADDRL = 0xff;
}
//========================================================================
// 函数: void EEPROM_Trig(void)
// 描述: 触发EEPROM操作.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_Trig(void)
{
F0 = EA; //保存全局中断
EA = 0; //禁止中断, 避免触发命令无效
IAP_TRIG = 0x5A;
IAP_TRIG = 0xA5; //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
//送完A5H后,IAP命令立即被触发启动
//CPU等待IAP完成后,才会继续执行程序。
_nop_(); //由于AI8051U是多级流水线的指令系统,触发命令后建议加4个NOP,保证IAP_DATA的数据完成准备
_nop_();
_nop_();
_nop_();
EA = F0; //恢复全局中断
}
//========================================================================
// 函数: void EEPROM_read_n(u32 EE_address,u8 *DataAddress,u16 number)
// 描述: 从指定EEPROM首地址读出n个字节放指定的缓冲.
// 参数: EE_address:读出EEPROM的首地址.
// DataAddress: 读出数据放缓冲的首地址.
// number: 读出的字节长度.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void EEPROM_read_n(u32 EE_address,u8 *DataAddress,u16 number)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_READ(); //送字节读命令,命令不需改变时,不需重新送命令
do
{
IAP_ADDRE = (u8)(EE_address >> 16); //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRH = (u8)(EE_address >> 8);//送地址中字节(地址需要改变时才需重新送地址)
IAP_ADDRL = (u8)EE_address; //送地址低字节(地址需要改变时才需重新送地址)
EEPROM_Trig(); //触发EEPROM操作
*DataAddress = IAP_DATA; //读出的数据送往
EE_address++;
DataAddress++;
}while(--number);
DisableEEPROM();
}
//========================================================================
// 函数: void EEPROM_SectorErase(u32 EE_address)
// 描述: 把指定地址的EEPROM扇区擦除.
// 参数: EE_address:要擦除的扇区EEPROM的地址.
// 返回: non.
// 版本: V1.0, 2013-5-10
//========================================================================
void EEPROM_SectorErase(u32 EE_address)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_ERASE(); //宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令
//只有扇区擦除,没有字节擦除,512字节/扇区。
//扇区中任意一个字节地址都是扇区地址。
IAP_ADDRE = (u8)(EE_address >> 16); //送扇区地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRH = (u8)(EE_address >> 8);//送扇区地址中字节(地址需要改变时才需重新送地址)
IAP_ADDRL = (u8)EE_address; //送扇区地址低字节(地址需要改变时才需重新送地址)
EEPROM_Trig(); //触发EEPROM操作
DisableEEPROM(); //禁止EEPROM操作
}
//========================================================================
// 函数: void EEPROM_write_n(u32 EE_address,u8 *DataAddress,u16 number)
// 描述: 把缓冲的n个字节写入指定首地址的EEPROM.
// 参数: EE_address:写入EEPROM的首地址.
// DataAddress: 写入源数据的缓冲的首地址.
// number: 写入的字节长度.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void EEPROM_write_n(u32 EE_address,u8 *DataAddress,u16 number)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_WRITE(); //宏调用, 送字节写命令
do
{
IAP_ADDRE = (u8)(EE_address >> 16); //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRH = (u8)(EE_address >> 8);//送地址中字节(地址需要改变时才需重新送地址)
IAP_ADDRL = (u8)EE_address; //送地址低字节(地址需要改变时才需重新送地址)
IAP_DATA= *DataAddress; //送数据到IAP_DATA,只有数据改变时才需重新送
EEPROM_Trig(); //触发EEPROM操作
EE_address++; //下一个地址
DataAddress++; //下一个数据
}while(--number); //直到结束
DisableEEPROM();
}
讲解视频网址
https://www.bilibili.com/video/BV1eg1xBrEXP/?spm_id_from=333.1007.0.0
页:
[1]