实验题目:STC-BSP 余下API函数及综合运用
实验时间:2022.3.7(第3周周一)~2022.3.20(第4周周日)
实验地点:在规定时间中自主寻找合适实验地点并在规定时间内完成
实验条件:
1.STC-B学习板(2021暑假小学期每个学生自己做的)
2.个人电脑:自备
3.Keil、ISP、CH340驱动软件:自备
4.STC-BSP及Demo 程序:教师提供
5.“STC_B学习板”软件支持包使用说明:教师提供
6.C51语言语法:课程提供参考电子书籍,或自找自学
7.Demo1~Demo5源程序:教师提供(Demo3源程序在实验结束后提供)
8.Demo4需要的直流电机模块:工程中心C栋215助教处临时借用
9.Demo5需要的485通信连线:工程中心C栋215助教处临时借用
实验目的:
1.学习和熟悉STC-BSP(“STC-B学习板”板级软件开发支撑包)余下(实验1外)所有模块所有API函数及其使用;
2.掌握“事件”与事件的“回调函数”编程机制与使用;
3.认识“STC-B学习板”中的几种通信方法,熟悉并掌握使用;
4.学习在STC-BSP支撑下,设计应用程序。
实验内容:
1.阅读《“STC_B学习板”软件支持包使用说明》中余下(实验1外)所有模块说明,阅读实验二提供的Demo源程序(Demo1、2、4、5),编写程序测试模块提供的各个函数和方法。除实验1要求熟悉的正向函数使用方法外,本实验重点是“事件”、事件的“回调函数”方法;
2.阅读Demo5相关资料与程序,与同学合作验证Demo 5完成的通信与组网功能,学习和了解相关原理,记录实验过程和现象。
3.实验2现场验收内容:设计与编写有一定功能、目的的应用程序,并调式和在“STC-B学习板”上实现;要求:含通信(串口1、串口2、485、或Ir)、含按键或导航按键操作。
4.选做:认识和验证Demo3功能(Demo3当前仅提供下载的HEX文件,参考源程序在实验二结束后提供),参考Demo2源码,看能否编写程序实现Demo3相同功能和效果?(实验二加分项,训练大家嵌入式应用程序、实用程序编写能力)
实验过程、步骤、现象、结果:
阅读所有余下的模块说明并测试其相关函数
1,ADC.h模块:
ADC模块用于“STC-B学习板”上与ADC相关电路:温度Rt、光照Rop、导航按键Nav、扩展接口EXT上的ADC转换。提供ADC模块初始化函数、2个应用函数,2个事件。
初始化函数:AdcInit(char ADCsel)
函数参数: ADCsel选择扩展接口EXT是否用作ADC功能,取值有
ADCincEXT : 含对扩展接口EXT设置ADC功能(EXT上P1.0、P1.1不可作数字IO功能使用)
ADCexpEXT : 不含对扩展接口EXT设置ADC功能(EXT上P1.0、P1.1可作数字IO功能使用)
选择前者可以使用扩展模块,后者则无法使用
2个应用函数:
struct_ADC GetADC():获取ADC值。ADC结构体信息如下:
typedef struct //ADC转换结果
{ unsigned int EXT_P10; // 扩展接口EXT上P1.0脚ADC(10bit)
unsigned int EXT_P11; // 扩展接口EXT上P1.1脚ADC(10bit)
unsigned int Rt; // 热敏电阻上ADC(10bit)
unsigned int Rop; // 光敏电阻上ADC(10bit)
unsigned int Nav; // 导航按键上ADC(10bit)
} struct_ADC;可用于获取扩展模块的数据或各个ADC相关电路的数据
char GetAdcNavAct(char Nav_button):获取导航按键(包含K3)状态
函数参数:Nav_button:指定要获取状态的导航按键。取值:
enumAdcNavKey3(K3键),
enumAdcNavKeyRight(右按),
enumAdcNavKeyDown(下按),
enumAdcNavKeyCenter(中心按),
enumAdcNavKeyLeft(左按),
enumAdcNavKeyUp(上按).
注意在使用ADC模块后Key模块的Key3相关的函数命令则无法使用
提供的两个事件为:
导航按键事件:enumEventNav
当导航按键5个方向或按键K3有任意”按下“或”抬起“动作时,将产生一个”导航按键事件“enumEventNav。响应导航按键事件的用户回调函数由用户编写,并由sys提供的SetEventCallBack()函数设置响应函数.
扩展接口EXT上P1.0、P1.1两个端口有新的AD值事件:enumEventXADC
当ADC模块对P1.0、P1.1进行ADC转换,获得了它们新的ADC结果时,将产生enumEventXADC事件,通知用户进行处理。响应enumEventXADC事件的用户回调函数由用户编写,并有sys提供的SetEventCallBack()函数设置响应函数.
ADC模块对P1.0、P1.1进行ADC转换速度为3mS,也即每3mS或每秒钟333次转换.
函数功能测试:
将ADC模块的温度,光强和Nav值输出在数码管中:
测试结果如下:其中3~6位表示光强,在用物品遮挡和放置强光下时结果会相应的升高降低,Nav的值在我们按下不同的导航键时会显示不同的值,这些值对应导航键每一个按键的状态信息,温度还没有转成摄氏度,目前显示的室内的ADC值为550左右,在加热后(用手)该数值降低
导航按键事件的回调测试:
在导航按键按下不同位置时,Led灯会亮起如代码所示相应位置的LED灯。测试结果符合预期:
2.DS1302.h模块:
DS1302模块用于控制“STC-B学习板”上DS1302芯片操作。
DS1302提供RTC(实时时钟)和NVM(非易失存储器)功能(断电后,RTC和NVM是依靠纽扣电池BAT维持工作的)。其中RTC提供年、月、日、星期、时、分、秒功能;NVM提供31 Bytes非易失存储器功能(地址为:0~30)。
其中地址为30的单元被DS1302Init()函数用于检测DS1302是否掉电,用户不能使用
DS1302模块共提供1个驱动函数、4个应用函数:
(1) void DS1302Init(struct_DS1302_RTC time):DS1302驱动函数。使用DS1302,需用该函数初始化和驱动一次
函数参数:结构struct_DS1302_RTC time
如果DS1302掉电(初始化时检测RTC数据失效),则以参数time定义的时间初始化RTC
(2)struct_DS1302_RTC RTC_Read(void):读取DS1302内部实时时钟RTC内容
函数返回值:结构struct_DS1302(见结构struct_DS1302定义)
(3)void RTC_Write(struct_DS1302_RTC time) :写DS1302内部实时时钟RTC内容
函数参数:结构struct_DS1302 time(见结构struct_DS1302定义)
(4)unsigned char NVM_Read(unsigned char NVM_addr): 读取NVM一个指定地址内容
函数参数:
NVM_addr:指定非易失存储单元地址,有效值0~30(共31个单元)
函数返回值:当函数参数正常时,返回NVM中对应单元的存储数值(1Byte)
当函数参数错误时,返回enumDS1302_error
(5)unsigned char NVM_Write(unsigned char NVM_addr, unsigned char NVM_data):向NVM一个指定地址写入新值
函数参数:NVM_addr:指定非易失存储单元地址,有效值:0~30(共31个单元。
NVM_data:待写入NVM单元的新值(1Byte)
函数返回值:当函数参数正常时,返回enumDS1302_OK
当函数参数错误时,返回enumDS1302_error
结构struct_DS1302_RTC定义:
typedef struct
{ unsigned char second; //秒(BCD码,以下均为BCD码)
unsigned char minute; //分
unsigned char hour; //时
unsigned char day; //日
unsigned char month; //月
unsigned char week; //星期
unsigned char year; //年
} struct_DS1302_RTC;
BCD码为2进码十进数,简单来说对于4位的二进制来说,只采用00001001的数字其他舍弃就是最简单的BCD码即只表示010的数字a~f不进行表示
测试函数:
初始界面如图所示,掉电后结果不变,按下按键日期发生变化,同样掉电后结果不发生改变
测试非易丢存储器:
初始情况:断电后结果不变 按下按键后结果如图:断电后结果不变
3.EXT.h模块
EXT模块用于控制“STC-B学习板”上扩展接口EXT上相关操作。
EXT模块根据应用需要,在外接相应模块或部件后,可实现多种相应功能。这里提供部分应用驱动和API函数。
EXT模块这里提供1个驱动函数和若干个应用层API函数。
EXT模块的API函数不是同时有效的,而是根据初始化函数参数不同而分别有效。
测试enumEXTWeight()功能:
显示情况如下:初始值并不为0,这是因为函数本身没有进行清零标定的工作
所以后续想要使用该模块还需要进行标定工作。
测试enumEXTPWM功能:
效果为当pwm1比pwm2差距20及以上时电机会进行旋转,当pwm1>pwm2 时逆时针旋转,反之顺时针旋转
测试enumEXTDecode
实验结果无论顺时针或逆时针,只要改变旋转角度都会显示正值,当不旋转时显示0。
测试enumEXTUltraSonic超声波测距
测试结果如图能过正确显示超声波模块到障碍物的距离
4.Hall.h模块
Hall模块用于获取“STC-B学习板”上hall传感器状态。hall模块共提供1个加载函数、1个应用函数,一个Hall事件:enumEventHall
(1)HallInit():hall模块初始化函数
(2)unsigned char GetHallAct(void):获取hall事件。
函数返回值:
enumHallNull(无变化)
enumHallGetClose(磁场接近)
enumHallGetAway(磁场离开)
查询一次后,事件值变成enumEventHall (仅查询一次有效)
(3)hall传感器事件:
当Hall检测到有"磁场接近"或"磁场离开"事件时,将产生一个Hall传感器事件(enumEventHall).响应事件的用户处理函数由用户编写, 并有sys中提供的SetEventCallBack()函数设置事件响应函数.
测试函数:
实验结果:磁铁靠近时数码管会显示1,初始情况显示0
5.IR.h模块
IR模块用于控制“STC-B学习板”上红外发送与接收控制,支持PWM、PPM红外编码协议的发送,PWM红外编码的接收,可用于制作红外遥控器、红外通信等。
IR模块提供1个驱动函数、5个API应用函数、1个红外接收事件(enumEventIrRxd:红外Ir上收到一个数据包)。
IR模块已不与串口通信(uart和uart2)冲突,可用与它们同时工作。(以前冲突)
API函数:
(1) void IrInit(unsigned char Protocol):IR模块初始化函数。
函数参数:unsigned char Protocol,定义红外协议。
Protocol 暂仅提供取值:NEC_R05d(定义红外协议基本时间片时长 = 13.1Protocol uS)
(2) char IrTxdSet(unsigned char pt,unsigned char num):以自由编码方式控制IR发送:可用于编写任意编码协议的红外发送,如各种电器红外遥控器等
函数参数:unsigned char pt,指向待发送红外编码数据的首地址。编码规则如下:
码i红外发送时长,码i红外发送停止时长 //单位:协议基本时间片的个数值,最大255。如当协议基本时间片为0.56mS时,数值1代表0.56mS时长,3代表1.68mS时长
unsigned char num,待发送红外编码数据的大小(字节数)
函数返回值:enumIrTxOK:调用成功,即所设定的发送数据包请求已被系统sys正确接受,sys将尽硬件资源最大可能及时发送数据。
enumIrTxFailure:调用失败(主要原因是:红外发送正忙(上一数据包未发完)、或红外正在接收一个数据包进行中
(3) char IrPrint(void pt, unsigned char num)://以NEC的PWM编码方式发送数据,可用于符合该函数发送格式的部分电器遥控器;与GetIrRxNum()、SetIrRxd()配合,可进行红外双机通信;等
//红外发送数据格式为:引导码:发(16基本时间片),停(8基本时间片)。0.56mS时:发9mS、停4.5mS
数据编码:“0” – 发(1基本时间片),停(1基本时间片)
“1” – 发(1基本时间片),停(3基本时间片) 先发高位、后发低位
结束码:发(1基本时间片),停(1基本时间片)
//非阻塞函数,该函数从被调用到返回大约1uS左右时间,但所指定的数据经红外发送完毕则需要较长时间(每字节大约需要10mS量级时间)。
函数参数:void *pt :指定发送数据包位置(数据包不含引导码、结束码信息,仅待发送的有效数据)
unsigned char num:发送数据包大小(字节数,不含引导码、结束码)
函数返回值:enumIrTxOK:调用成功,即所设定的发送数据包请求已被系统sys正确接受,sys将尽硬件资源最大可能及时发送数据。
enumIrTxFailure:调用失败(主要原因是:红外发送正忙(上一数据包未发完)、或红外正在接收一个数据包进行中(同IrTxdSet()函数返回值)
(4) void SetIrRxd(void *RxdPt,unsigned char RxdNmax):设置红外接收数据包存放位置、每个数据包最大字节数。 收到一个数据包(至少1字节数据)时将产生numEventIrRxd事件。与它机IrPrint()函数配合,可实现红外数据通信
函数参数: void char *RxdPt:指定接收数据包存放区(首地址)
unsigned char RxdNmax:指定每个数据包接收最大字节数。接收数据如果字节数超出,超出部分将被忽略。该值定义接收数据包最大值,以免超出而影响程序其它数据
(5) unsigned char GetIrRxNum(void):获取收到的红外接收数据包实际字节数。与SetIrRxd()配合,可实现红外数据包接收。(它机应使用IrPrint()函数发送数据包)
返回取值 <= Ir接收缓冲区最大字节数(RxdNmax定义)。如果实际红外数据包字节数大于RxdNmax,收到的多余字节数将不被存储(也不进行计数)
函数返回值:红外接收数据包大小(字节数)。
当收到一个数据包的numEventIrRxd事件产生后,可用该函数获取红外接收数据包的大小(字节数)。其它时间访问,其值不确定
(6)char GetIrStatus(void): 获取Ir状态
函数返回值:enumIrFree:红外空闲
enumIrBusy:红外正忙(正在发送数据包,或正在接收数据包)
(7)红外接收事件enumEventIrRxd:红外Ir上收到一个符合格式的数据包(红外格式见IrPrint()函数说明)。如果接收数据包实际字节数大于Ir接收缓冲区最大字节数(RxdNmax定义),也在收到数据包结束符后产生enumEventIrRxd事件
测试函数:
测试结果如图,当按下Key1后收到红外信号后,数码管上的数字加一。
6.M24C02.h模块
M24C02模块用于控制“STC-B学习板”上IIC接口的非易失存储器(NVM)M24C02芯片操作。
M24C02提供2K bits(256 Bytes)非易失存储器(NVM)功能(非易失存储器单元地址为:00~0xff)。
M24C0402模块共提供2个应用函数(本模块不需要初始化)
(1)unsigned char M24C02_Read(unsigned char NVM_addr): 读取M24C02一个指定地址内容
函数参数: NVM_addr:指定非易失存储单元地址,有效值00~0xff(共256个单元)
函数返回值:返回M24C02中对应单元的存储数值(1Byte)
(2)void M24C02_Write(unsigned char NVM_addr, unsigned char NVM_data):向M24C02一个指定地址写入新值
函数参数:
NVM_addr:指定非易失存储单元地址,有效值:00~0xff(共256个单元)
NVM_data:待写入M24C02单元的新值(1Byte)
补充说明:
M24C02为非易失性存储器,其主要特点是:存储的内容在断电后能继续保存,一般用于保存断电需保留的工作系统参数;
但读、写M24C02内部每一个字节均需要花费一定时间(每次读写操作大约数十uS,写周期为5~10mS),且有”写“寿命限制(每一单元大约”写“寿命为10万次量级寿命);
与DS1302内部NVM区别:容量大(M24C02提供256字节,M24CXX系列最大可提供64K字节),但有“写”寿命限制(一般为数十万次“写”寿命“,且写周期长(5~10mS)因此,两次写操作之间需间隔5~10mS以上;
实验效果与DS1302类似都可以做到掉电保存效果
7.StepMotor.h模块
StepMotor用于STC-B板控制步进电机。共提供1个驱动函数、3个应用函数:(1)StepMotorInit():步进电机模块驱动函数
(2)SetStepMotor(char StepMotor,unsigned char speed ,int steps ) 指定步进电机、按指定转动速度、转动指定步
函数参数:StepMotor 指定步进电机,取值(enum StepMotorName中定义)
enumStepMotor1: SM 接口上的步进电机
enumStepMotor2: 此时,用L0~L3四个LED模拟一个4相步进电机
enumStepMotor3: 此时,用L4~L7四个LED模拟一个4相步进电机
Speed:步进电机转动速度(0~255),单位:步/S。 (实际每步时间=int(1000mS/speed) mS),与设置速度可能存在一定误差
Step:步进电机转动步数(-32768~32767),负值表示反转
函数返回:enumSetStepMotorOK: 调用成功(enum StepMotorActName中定义)
numSetStepMotorFail:调用失败(电机名不在指定范围,或speed=0,或调用时正在转动)
(3)EmStop(char StepMotor) 紧急停止指定步进电机转动
函数参数:StepMotor 指定步进电机。函数参数不对将返回0值。
函数返回:剩余未转完的步数
(4)GetStepMotorStatus(char StepMotor) 获取指定步进电机状态
函数参数:StepMotor 指定步进电机
函数返回:enumStepMotorFree:自由(enum StepMotorActName中定义)
enumStepMotorBusy,忙(正在转动)
enumSetStepMotorFail:调用失败(步进电机名不在指定范围)
实验效果:电机逆时针转动(但是速度为200了转速看起来还是很慢)
8.Uart2.h
Uart2模块提供Uart2模块初始化函数、3个应用函数,1个事件(enumUart2EventRxd):
(1) Uart2Init(unsigned long band,unsigned char Uart2mode):Uart2模块初始化函数。
函数参数:unsigned long band:定义串口2的通信波特率(单位:bps)(固定8个数据位、1个停止位,无奇偶校验位)
unsigned char Uart2mode:定义串口2位置
取值:Uart2UsedforEXT —— 串口2在EXT扩展插座上
Uart2Usedfor485 —— 串口2用于485通信(半双工。发送数据包时不能接收数据)
(2) void SetUart2Rxd(char *RxdPt, unsigned int Nmax, char *matchhead, unsigned int matchheadsize);
设置串口2接收参数:数据包存放位置、大小,包头匹配字符、匹配字符个数。收到符合条件的数据包时将产生enumEventRxd事件。
函数参数: char *RxdPt:指定接收数据包存放区(首地址)
unsigned int Nmax:接收数据包大小(字节数),最大65535
char matchhead: 需要匹配的数据包头(首地址)
unsigned int matchheadsize:需要匹配的字节数
补充说明:
Nmax=1:为单字节接收,即收到一个字节就产生enumEventUart2Rxd事件(如果定义了匹配,还需满足匹配条件);
0 < matchheadsize < Nmax:要求接收数据中连续matchheadsize个字节与matchhead处数据完全匹配,才在收到Nmax数据时产生enumEventRxd事件;
matchheadsize = Nmax:设定接收数据包完全匹配
matchheadsize=0 或 matchheadsize > Nmax:将不做匹配,接收到任意Nmax数据时产生enumEventRxd事件;
在enumEventRxd事件发出后,接收到的数据包应及时使用或取出,收到下一个数据时将破坏和覆盖前面收到的数据包
(3)char Uart2Print(void *pt, unsigned int num):发送数据包,非阻塞函数(即函数不等到所设定任务全部完成才返回),该函数从被调用到返回大约1uS左右时间。
函数参数: void *pt :指定发送数据包位置
unsigned int num:发送数据包大小;
函数返回值:enumTxOK:调用成功,即所设定的发送数据包请求已被系统sys正确接受,sys将尽硬件资源最大可能及时发送数据。
enumTxFailure:调用失败(主要原因是:串口正忙(上一数据包未发完)
补充说明:串口上发送1个字节数据大约需要时间0.1mS~10mS(视所设定的波特率)。
(4)char GetUart2TxStatus(void): 获取Uart2发送状态
函数返回值:enumUart2TxFree:串口2发送空闲
enumUart2TxBusy,串口2发送正忙
(5) Uart2接收事件:enumUart2EventRxd。表示收到了一个符合指定要求(数据包头匹配、数据包大小一致)的数据包。
补充说明:串口(1和2)上收到的两个数据包之时间间隔要求不小于1mS(原因:系统内部调度方法限制)
测试函数:蓝牙方面的测试:
完成初始化后就可以接收到蓝牙模块进行配对了
测试485情况下的代码:
当Key1按下后对方的板子上数码管数字会加一,实验结果:
9.Uart1.h模块
Uart1模块提供Uart1模块初始化函数、3个应用函数,1个事件(enumEventRxd):
(1) Uart1Init(unsigned long band):Uart1模块初始化函数。
函数参数:unsigned long band定义串口1的通信波特率(单位:bps)(8个数据位、1个停止位,无奇偶校验位)
函数返回值:无
(2) void SetUart1Rxd(char *RxdPt, unsigned int Nmax, char *matchhead, unsigned int matchheadsize);
设置串口1接收参数:数据包存放位置、大小,包头匹配字符、匹配字符个数。收到符合条件的数据包时将产生enumEventRxd事件。
函数参数: char *RxdPt:指定接收数据包存放区(首地址)
unsigned int Nmax:接收数据包大小(字节数),最大65535
char matchhead: 需要匹配的数据包头(首地址)
unsigned int matchheadsize:需要匹配的字节数
补充说明:
Nmax=1:为单字节接收,即收到一个字节就产生enumUart1EventRxd事件(如果定义了匹配,还需满足匹配条件);
0 < matchheadsize < Nmax:要求接收数据中连续matchheadsize个字节与matchhead处数据完全匹配,才在收到Nmax数据时产生enumEventRxd事件;
matchheadsize = Nmax:设定接收数据包完全匹配
matchheadsize=0 或 matchheadsize > Nmax:将不做匹配,接收到任意Nmax数据时产生enumEventRxd事件;
在enumEventRxd事件发出后,接收到的数据包应及时使用或取出,收到下一个数据时将破坏和覆盖前面收到的数据包
函数返回值:无
(3)char Uart1Print(void *pt, unsigned int num):发送数据包,非阻塞函数(即函数不等到所设定任务全部完成才返回),该函数从被调用到返回大约1uS左右时间。
函数参数: void *pt :指定发送数据包位置
unsigned int num:发送数据包大小;
函数返回值:enumTxOK:调用成功,即所设定的发送数据包请求已被系统sys正确接受,sys将尽硬件资源最大可能及时发送数据。
enumTxFailure:调用失败(主要原因是:串口正忙(上一数据包未发完)
补充说明:串口上发送1个字节数据大约需要时间0.1mS~10mS(视所设定的波特率)。
(4)char GetUart1TxStatus(void): 获取Uart1发送状态
函数返回值:enumUart1TxFree:串口1发送空闲
enumUart1TxBusy,串口1发送正忙
(5) Uart1接收事件:enumEventUart1Rxd。表示收到了一个符合指定要求(数据包头匹配、数据包大小一致)的数据包。
补充说明:串口(1和2)上收到的两个数据包之时间间隔要求不小于1mS(原因:系统内部调度方法限制)
测试函数:
按下按键后,电脑可从串口(初始化时波特率要与串口助手一致)得到1,1,1,1,1,1的数据(6个1是发送数据地址中的数据)
电脑先串口发送数据后板子接收到信息后触发回调函数,板子上数码管的数字加一。
实验效果:
10.Vib.h模块
Vib用于获取"STC-B学习板"上Vib传感器状态.提供一个模块加载函数和一个应用函数,一个Vib事件enumEventVib:
(1)VibInit():振动传感器Vib模块初始化函数;
(2)char GetVibAct():获取Vib事件。
函数返回值:返回当前Vib传感器事件,返回值:enumVibNull——无,enumVibQuake——发生过振动
查询一次后,事件值变成 enumVibNull (仅查询一次有效)
(3)Vib传感器事件enumEventVib:
当Vib检测到有”振动“事件时,将产生一个”振动事件“,响应事件的用户处理函数由用户编写,
并有sys中提供的SetEventCallBack()函数设置.
测试函数:
当板子每震动一次,数码管上的数字加一
实验效果:每次震动时数字加一(有时不稳定会加2)
阅读实验二提供的Demo源程序(Demo1、2、4、5)
1,Demo1:Demo1主要代码:
该回调函数使用GetADC(),获取温度,光强等属性值,然后将其中的光照强度进行输出在数码管中,然后进行判断如果光强大于50就会将LED左边的灯点亮并发出警报,小于20时将LED右边的灯点亮并发出警报,不过警报频率较小
2,Demo2:Demo2主要代码:
两个回调函数,按键回调函数通过检测按键1和按键3的按下情况将各自的标志位改变,如果标志位为0,按下后(相当于此时开始计时)会将前面的计数清零,蜂鸣器同时会发声。时间回调函数负责再标志位为1时,每次调用时间回调函数时进行计数加一,并将计数结果显示在数码管上。
3,Demo4:Demo4主要代码:
该主要代码使用SetPWM()API函数发送指定占空比和频率的数据控制电机进行旋转,按下按键后可以通过改变占空比来控制电机旋转。时间回调函数则进行SetPWM()的设置(通过将占空比进行交换做到将发送信号的数据频率相反的功能,从而使电机逆反向旋转)同时将电机占空比输出到数码管上。
4,Demo5:Demo5主要代码:
设置时通过在校验码中加入myID变量,来确定多线连接可以准确向目标板子发送数据,数据到达后取出其中的后三位(前3位为校验码)分别为旋转方向和速度
然后通过时间回调函数触发电机的设置来完成将收集到的数据应用到电机中,同时在本地板端可以通过key1按键来改变dir从而触发电机旋转的反向。
阅读Demo5相关资料与程序,与同学合作验证Demo 5完成的通信与组网功能,学习和了解相关原理,记录实验过程和现象。
在Demo5中将汇聚节点代码烧录到一个板子中与电脑连接,485的代码烧录到另一个板子中。如此在串口1中输入数据aa 55 01 01 40 00,那么汇聚节点会转发到串口2,485通信中,电机控制属性与其一致。
结果如图所示:这里没有电机将数据结果显示在数码管上,第二个电机中只需在串口1的数据中将第3个字节改为02即可
红外测试:将ID改为0x82,汇聚节点会将数据转发给红外进行发送,结果如图所示显示
方向001。
设计与编写有一定功能、目的的应用程序,并调式和在“STC-B学习板”上实现;
简单功能案例:
按下一个 板子上的key1后另一个接收到到红外信号的板子,通过判断发送的数据是否正确(全为1)如果正确则数码管上的数字加一
实验效果:一个板子按下5次key1另一个按下3次
较为复杂功能案例:
在学习板上按键key3为切换单机和联机模式,初始为单机模式,在单机情况下游戏初始界面如下图:
此界面为游戏准备阶段右边的7个数码管为上次游戏所得分数这里初始为0,最左边的数码管显示的是“拼图碎片”,在游戏开始时如上图右图所示通过导航按键的上下可以操作最左边的“拼图碎片”同时左边会随机出现一个缺一角的“图片”当左边的拼图碎片与右边的图形合在一起恰好形成一个带小数电的数字8时(即合起来恰好数码管全点亮)则得一分,并继续游戏,在游戏中按下key1会暂停游戏,这时数码管上回固定你的拼图碎片同时在右侧显示出目前的得分,再次按下key1后可以继续游戏(此时会触发游戏暂停惩罚图形会往前移一格)。若失败则学习板发出提醒声音游戏结束,在右侧显示本次游戏的得分(如下图)。(与暂停时情况类似但此时key1无法开始游戏),在游戏任何时候按下key2可以重新开始游戏(但分数会归0)。
在游戏进行时随着游戏得分的增加,图形移动速度也会增加,得分每多5分游戏速度会升一级最高5级。
游戏任何时候按下key3切换模式,切换到联机模式时,在联机模式中双方初始状态如下
左侧数码管显示为双方目前的得分,右边的两个数码管靠左的数码管显示的是设置的难度等级,最右边的数码管显示的是对方下一次游戏时会出现的图形。此时通过key1,key2可以对难度和图形进行设置,设置完成后往下按下导航按键,此时对方进入游戏,出现的难度和图形与自己设置一致,设置难度越高对方成功拼图后得分也越高,对方若失败则学习板发出提醒声音对方游戏结束
对方此时不能再进入游戏得分但可以继续设置难度和图形发送,当双方玩家均失败后得分高的玩家会播放胜利音乐,若分数相同则双方都播放音乐。播放音乐过程中或播放结束都可以重新开始游戏,而一旦任何一方重新开始游戏双方分数都会清零然后重新开始游戏。
工程代码:
#include "STC15F2K60S2.H" //必须。
#include "sys.H" //必须。
#include "displayer.H" //可选,如果使用显示模块
#include "Key.H" //可选,如果使用按键模块
#include "beep.h" //可选,如果使用蜂鸣器模块
#include "music.h" //可选,如果使用音乐播放功能
#include "ADC.h" //可选,如果使用ADC模块(含EXT上ADC)
#include "uart2.h" //可选,如果使用串口2通信(485通信、或EXT上串口)
code unsigned long SysClock=11059200; //必须。定义系统工作时钟频率(Hz),用户必须修改成与实际工作频率(下载时选择的)一致
#ifdef _displayer_H_ //显示模块选用时必须。(数码管显示译码表,用戶可修改、增加等)
//******* 用户程序段2:用户自定义函数声明 *************//
#ifdef _displayer_H_ //显示模块选用时必须。(数码管显示译码表,用戶可修改、增加等)
code char decode_table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x08,0x40,0x01, 0x41, 0x48,
/* 序号: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
/* 显示: 0 1 2 3 4 5 6 7 8 9 (无) 下- 中- 上- 上中- 中下- */
0x3f|0x80,0x06|0x80,0x5b|0x80,0x4f|0x80,0x66|0x80,0x6d|0x80,0x7d|0x80,0x07|0x80,0x7f|0x80,0x6f|0x80,
/* 带小数点 0 1 2 3 4 5 6 7 8 9 */
0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,~0xbf,~0xdf,~0xef,~0xf7,~0xfb,~0xfd,~0xfe};
//******* 用户程序段3:用户程序全局变量定义 *************//
#include "song.c" //举例。song.c中编写了音乐(歌唱祖国)编码
unsigned char d[8] = {33, 0, 0, 0, 0, 0, 0, 0};
int score1 = 0, score2 = 0;
char format, flag, fin, flag2, diff = 1; //0 alone 1 togater 0 start 1 end
unsigned char OK = 1, OK2 = 1; //联机模式判断是否挂了 1没挂 0挂了
//flag2 0 处于发题阶段 1处于游戏阶段
char disp = 26;
unsigned char datao[7]; //举例。通信(串口1、串口2、红外共用)缓冲区8字节
unsigned char datahead[2] = {0xaa, 0x55}; //校验位
char loc = 7, val7 = 26, val6 = 1;
int time = 0;
int delay = 100;
char normal = 0; //0 正常接送状态 1 异常接受状态
char First=1;
void updatadisp() //显示更新函数
{
Seg7Print(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7]);
}
void PlayGame() //玩游戏
{
int mid = score1;
int i;
if (flag == 1) //游戏结束阶段
{
for (i = 0; i < 7; i++)
{
d[7 - i] = (char)(mid % 10);
mid = mid / 10;
}
}
if (flag == 0)
{
for (i = 1; i < 8; i++)
d[i] = 10;
d[loc] = disp;
if (loc == 1)
{
if (d[0] + disp == 65)
{
score1++;
loc = 7;
disp = rand() % 7 + 26;
}
else
{
SetBeep(2000, 50);
flag = 1;
fin = 1;
}
}
else
{
loc--;
}
}
}
void PlayGameTogater()
{
int i, mid;
if (flag2 == 0) //双方发题阶段
{
mid = score1;
for (i = 0; i < 4; i++)
{
d[3 - i] = (char)(mid % 10);
mid = mid / 10;
}
d[4] = 10;
d[5] = 10;
d[6] = val6;
d[7] = val7;
if (OK == 0 && OK2 == 0)
{
if (score1 >= score2)
{
SetMusic(enumModeInvalid, enumModeInvalid, &song, sizeof(song), enumMscDrvLed);
SetPlayerMode(enumModePlay);
}
OK = 1;
First=1;
}
}
else //游戏阶段跑动中需要添加延时
{
SetPlayerMode(enumModeStop);
LedPrint(0);
for (i = 1; i < 8; i++)
d[i] = 10;
d[loc] = disp;
if (loc == 1)
{
loc = 7;
if (d[0] + disp == 65)
{
OK = 1;
if (diff <= 5)
score1 += diff;
else if (diff <= 8)
score1 += diff * 2;
else
score1 += diff * 3;
}
else
{
OK = 0;
SetBeep(2000, 50);
datao[0] = 0xaa; //校验码0xaa,0x55
datao[1] = 0x55;
datao[2] = d[6];
datao[3] = d[7];
datao[4] = score1;
datao[5] = OK;
datao[6] = 1; //异常发送
Uart2Print(&datao, sizeof(datao));
}
flag2 = 0;
}
else
loc--;
}
}
void Play_callback() //游戏回调函数
{
if (format == 0)
{
if (flag == 0)
{
time++;
if (time == delay)
{
PlayGame();
time = 0;
if (score1 < 5)
delay = 100;
else if (score1 < 10)
delay = 80;
else if (score1 < 15)
delay = 50;
else if (score1 < 20)
delay = 40;
else if (score1 < 25)
delay = 35;
else
delay = 30;
}
}
else
PlayGame();
}
if (format == 1)
{
if (flag2 == 0)
PlayGameTogater();
else
{
time++;
if (time == delay)
{
PlayGameTogater();
time = 0;
}
}
}
}
void mykey_callback() //按键回调
{
if (GetKeyAct(enumKey1) == enumKeyPress)
{
SetBeep(1000, 10);
if (format == 0 && fin == 0)
{
flag = !flag;
}
if (format == 1 && flag2 == 0) //联机且在准备阶段
{
if (val7 == 32)
val7 = 26;
else
val7++;
}
}
if (GetKeyAct(enumKey2) == enumKeyPress)
{
SetBeep(1000, 10);
if (format == 0)
{
fin = 0;
loc = 7;
score1 = 0;
disp = rand() % 7 + 26;
delay = 100;
flag = 0;
time = 0;
}
if (format == 1 && flag2 == 0)
{
if (val6 == 9)
val6 = 1;
else
val6++;
}
}
}
void myKN_callback() //导航按键回调
{
if (GetAdcNavAct(enumAdcNavKey3) == enumKeyPress)
{
SetBeep(1000, 10);
format = !format;
fin = 0;
loc = 7;
score1 = 0;
delay = 100;
flag = 1;
time = 0;
if (format == 0)
d[0] = 33;
}
if (GetAdcNavAct(enumAdcNavKeyUp) == enumKeyPress && ((!format && flag == 0) || (format && flag2 == 1)))
{
SetBeep(1000, 10);
if (d[0] == 39)
d[0] = 33;
else
d[0] = d[0] + 1;
}
if (GetAdcNavAct(enumAdcNavKeyDown) == enumKeyPress && ((!format && flag == 0) || (format && flag2 == 1)))
{
SetBeep(1000, 10);
if (d[0] == 33)
d[0] = 39;
else
d[0] = d[0] - 1;
}
if (GetAdcNavAct(enumAdcNavKeyCenter) == enumKeyPress && format == 1 && flag2 == 0) //联机模式且处于发题阶段
{
SetBeep(1000, 10);
if(First)//第一次发生时
{
score1=0;
score2=0;
First=0;
}
time = 0;
datao[0] = 0xaa; //校验码0xaa,0x55
datao[1] = 0x55;
datao[2] = d[6];
datao[3] = d[7];
datao[4] = score1;
datao[5] = OK;
datao[6] = 0; //正常发送
Uart2Print(&datao, sizeof(datao));
}
}
void myUart2Rxd_callback() //485信息回调
{
if (format == 1 && flag2 == 0) //联机模式且处于发题阶段
{
normal = datao[6];
if (normal == 0 && OK == 1) //正常
{
SetBeep(1000, 20);
if(First)
{
score1=0;
score2=0;
First=0;
}
OK2 = datao[5];
disp = datao[3];
diff = datao[2];
score2 = datao[4];
switch (diff)
{
case 1:
delay = 100;
break;
case 2:
delay = 90;
break;
case 3:
delay = 80;
break;
case 4:
delay = 70;
break;
case 5:
delay = 60;
break;
case 6:
delay = 50;
break;
case 7:
delay = 45;
break;
case 8:
delay = 40;
break;
case 9:
delay = 30;
break;
}
flag2 = 1;
d[0] = 33;
PlayGameTogater();
}
else //异常
{
// SetBeep(2000, 10);
OK2 = datao[5];
score2 = datao[4];
}
}
}
void main()
{
AdcInit(ADCexpEXT);
MySTC_Init();
DisplayerInit();
Key_Init();
BeepInit();
MusicPlayerInit();
flag = 1; //游戏结束
format = 0; //单机
datao[5] = 1;
SetDisplayerArea(0, 7);
Seg7Print(34, 0, 0, 0, 0, 0, 0, 0);
PlayTone(enumModeInvalid, enumModeInvalid, &song, sizeof(song));
SetMusic(enumModeInvalid, enumModeInvalid, &song, sizeof(song), enumMscDrvLed);
Uart2Init(2400, Uart2Usedfor485);
SetUart2Rxd(&datao, sizeof(datao), datahead, sizeof(datahead));
SetEventCallBack(enumEventKey, mykey_callback);
SetEventCallBack(enumEventNav, myKN_callback);
SetEventCallBack(enumEventSys1mS, updatadisp);
SetEventCallBack(enumEventSys10mS, Play_callback);
SetEventCallBack(enumEventUart2Rxd, myUart2Rxd_callback);
while (1)
{
MySTC_OS();
}
}
认识和验证Demo3功能(Demo3当前仅提供下载的HEX文件,参考源程序在实验二结束后提供),参考Demo2源码,看能否编写程序实现Demo3相同功能和效果?
实验代码:
#include "STC15F2K60S2.H"
#include "displayer.h"
#include "beep.h"
#include "Key.h"
#include "sys.h"
code unsigned long SysClock=11059200;
#ifdef _displayer_H_
code char decode_table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00,0x08,0x40,0x01, 0x41, 0x48,
0x3f|0x80,0x06|0x80,0x5b|0x80,0x4f|0x80,0x66|0x80,0x6d|0x80,0x7d|0x80,0x07|0x80,0x7f|0x80,0x6f|0x80 };
unsigned int time1,time2;
bit flag_t1=0,flag_t2=0;
void mykey()
{ if (GetKeyAct(enumKey3) == enumKeyPress)
{ flag_t1=~flag_t1;
if (flag_t1) time1=0;
SetBeep(1000,20);
}
if (GetKeyAct(enumKey1) == enumKeyPress)
{ flag_t2=~flag_t2;
if (flag_t2) time2=0;
SetBeep(1500,20);
}
}
void my10mSfun()
{ unsigned char d0,d1,d2,d3,d4,d5,d6,d7;
if(flag_t1 ==1) time1++;
if(flag_t2 ==1) time2++;
d0=time1/1000%10; if (d0==0) d0=10;
d1=time1/100%10+16;
d2=time1/10%10;
d3=time1%10;
d4=time2/1000%10;if (d4==0) d4=10;
d5=time2/100%10+16;
d6=time2/10%10;
d7=time2%10;
Seg7Print(d0,d1,d2,d3,d4,d5,d6,d7);
}
void main()
{ DisplayerInit();
BeepInit();
Key_Init();
SetEventCallBack(enumEventSys10mS, my10mSfun);
SetEventCallBack(enumEventKey,mykey);
SetDisplayerArea(0,7);
LedPrint(0);
MySTC_Init();
while(1)
{ MySTC_OS();
}
}
测试效果:按下Key1或者key2后,开始响起1S的预备声音,如果按下的时间小于1秒则不会触发开始指令,当时间大于1S时松开按键则开始双通道计时开始,按下key1,key2可以停止相关通道计时,当两个通道都停止时,再次按下Key1或Key3开始重新计时,原来是计时结果清零,然后重复以上步骤。
实验遇到的问题和难点
1,老师demo中串口2的事件在注释的最后写错了
2,按键状态获取函数GetKeyAct()在使用一次后状态会变为NULL。
这里需要通过一些编程方法来解决,比如可以像上面demo3的复现代码中的一样用额外的一个变量来将可能多次运行的情况分开以避免函数失效的情况。
实验收获、体会、改进意见
本次实验内容较多,一方面熟悉了之前在小学期的相关模块操作,更多的是补充了小学期作业中没有使用的模块知识