设备:DM164137开发板 PIC16F18446主芯片。
目标为实现任务1:通过按键扩展功能,分为按键点击(快速点击),短按(大于1秒),长按(大于5秒)用来控制板上LED显示。同时外接串口模块打印信息(包括按键信息,电位计信息)
思路:任务要求,分三个状态,1 点击状态、2 短按状态、3 长按状态。三种状态通过按键不同的行为进行切换。板子上有4颗LED灯。最左边的led用来显示按键的状态,按下则亮起。剩下3颗led灯,用来显示当前的状态。上电后默认状态为1。开启定时器0,当按键按下后开始计时,抬起后计时结束。判断计时长度,决定状态值。启动ADC,用来读取电位器的值。开启串口,开发板上烧写器中集成了个串口,但是不懂开发板的设计思路,怎么弄都无法使用。只能外接一个ch340模块,接串口到电脑来读取串口信息。
使用MCC来配置单片机,挺好用的,很多代码都帮忙生成了。而且是图形化界面,不用过多地和底层寄存器打交道了。
总体端口使用情况。
ADC的配置基本没做修改。
系统时钟,使用32M。
启动tim0时钟,允许中断,1ms为一个计时单位。
串口1,使用57600波特率,留意开启"redirect stdio to usart"这样就能使用printf函数了。
用MCC设置好上边的一切,剩下的就简单很多了。在main的文件中,自定义一个时间中断处理函数。当按键被按下后,启动time0开始计时,每1ms中断一次,中断后进入中断函数,将按键按下的时间加一。当按键被松开后,停止计时。主循环通过判断按键被按下的时长,即可获得当前板子的状态。
uint16_t keyCleckTime=0; //按键被按下的时长
void TMR0_Handler(void) //1ms
{
keyCleckTime++;
}
//判断当前系统状态 返回 1、2、3
uint8_t judgeStat(){
if(keyCleckTime>5000)
return 3;
if(keyCleckTime>1000)
return 2;
return 1;
}
void main(void)
{
uint16_t adcVal=0,systimes=0; //电位器的值
uint8_t curstat,flag=1;
// initialize th device
SYSTEM_Initialize();
TMR0_SetInterruptHandler(TMR0_Handler); //注册中断回调函数
INTERRUPT_GlobalInterruptEnable();
while (1)
{
curstat=judgeStat();
//根据系统不同的状态 显示LED灯,并通过串口上送 状态信息系
if(curstat==1){
LED0_SetHigh();
LED1_SetLow();
LED2_SetLow();
}
if(curstat==2){
LED1_SetHigh();
LED0_SetLow();
LED2_SetLow();
}
if(curstat==3){
LED2_SetHigh();
LED1_SetLow();
LED0_SetLow();
}
while(KEY_GetValue()==LOW){ //按键被按下
if(flag==1){
LED3_SetHigh();
keyCleckTime=0;
TMR0_StartTimer(); //开启计时器
}
flag=0;
__delay_ms(1);
}
if(flag==0){ //按键被松开
LED3_SetLow();
TMR0_StopTimer();
flag=1;
}
systimes++;
__delay_ms(5);
if(systimes>=50){
systimes=0;
adcVal=ADCC_GetSingleConversion(channel_ANC0);
printf("Curr stat:%d, button push time=%d ms, ADC=%d\r\n",curstat,keyCleckTime,adcVal);
}
}
}
学习过程中遇到的问题:
1 自定义时间中断后,需要注册到系统中去。一开始使用MCC,开启了时间中断,怎么也不明白如何进入中断,参考了网友的代码,才明白。
2 板子上VCOM实在不懂是干嘛用的。插入usb口能发现一个串口,但是哪怕是短接板子上的VCOM的TX,RX也无法实现自收发,只能放弃使用。
3 一开始是有想用按键中断的,通过MCC配置发现,这个芯片的外部中断似乎只能是 上沿 或者下沿中断,没找到上下沿中断。以前学习stc51的8位机时是有上下沿都中断的。这个貌似没有,遂放弃。