FreeRTOS项目(2)摇杆按键检测
PS:最近一个多月以来一直在赶工作上的项目,最近才抽空把摇杆的文章补完。。。
在上一章我们准备好了一个模板,一个骨架,今天我们在学习如何在FreeRTOS上读取摇杆的方位。
目录
模块简介
代码实现
ADC初始化
ADC转换值获取
按键(Z轴)检测
main.c
执行效果
模块简介
下图为本人所使用的模块。
如图所示,这个模块有五个引脚,分别是电源、GND、X轴、Y轴 和 Z轴(SW)。
电源方面,虽然标注了5V,但是3.3V一般就能驱动,如果3.3V带不动的情况下再改接5V。
模块X轴和Y轴采用的是ADC模拟量检测,而按下摇杆所触发Z轴的是数字量,即高低电平。我把按下摇杆作为确定键来使用。
代码实现
ADC初始化
因为程序是通过RTOS调度运行的,所以在这里省去了中断的相关配置。其次为了使用灵活,通道转换顺序放在其他位置定义了。
void ADCx_Config(void)
{ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );ADC_APBxClock_FUN ( ADC_CLK, ENABLE );RCC_ADCCLKConfig(RCC_PCLK2_Div6); GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = ROCKERBAR_PIN;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(ADC_PORT, &GPIO_InitStructure); ADC_InitTypeDef ADC_InitStructure;//独立模式ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//扫描模式:关ADC_InitStructure.ADC_ScanConvMode = DISABLE;//连续转换模式:关 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//右对齐ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//转换通道数量:1个ADC_InitStructure.ADC_NbrOfChannel = 1; //外部触发源:无,软件自动触发ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_Init(ADC1, &ADC_InitStructure);ADC_Cmd(ADC1, ENABLE);//初始化校准寄存器并等待初始化完成ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);//开启校准并等待校准完成ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);}
ADC转换值获取
基于上述的初始化操作,我们只需要控制ADC的转换通道就能实现任意通道的ADC值获取。因此,下面的函数实现了根据通道来单次转换ADC值的功能。
uint16_t adc_value;uint16_t AD_GetValue(uint8_t ADC_Channel)
{//配置ADC的转换顺序和采样时间ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);//软件触发转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);//等待转换完成标志位置位并返回转换值while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);return ADC_GetConversionValue(ADC1);
}
按键(Z轴)检测
通过前面的ADC我们就能获取摇杆四个方向的输入,Z轴实际上就是按下按键,因此我们需要一个按键检测函数。
首先是GPIO初始化。
void Key_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK,ENABLE);GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);
}
然后是按键检测函数。
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{ if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON ) { while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON); return KEY_ON; }elsereturn KEY_OFF;
}
到这一步,外设的配置工作基本就完成了,接下来就是添加到实现逻辑里。
main.c
首先在硬件初始化函数Hardware_Config()中加入ADC和按键的初始化。
以下为摇杆检测任务。
该任务的功能就是每1000ms(1s)检测一次摇杆操作,并将摇杆操作状态通过串口打印。四个方向和按下在同一时刻只能存在一种情况。
void RockerBar_Task(void * param)
{uint8_t confirm_flag = 0;while(1){XValue = AD_GetValue(ADC_Value1);YValue = AD_GetValue(ADC_Value2);if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3) == 0){confirm_flag = 1;}if(confirm_flag == 1){printf("Confirm\r\n");}else if(((XValue >= 2000) && (XValue <= 2200)) && ((YValue >= 2000) && (YValue <= 2200)) && (confirm_flag == 0){printf("No change in position\r\n");}else if(((XValue >= 0) && (XValue <= 10)) && ((YValue >= 2000) && (YValue <= 2200))){printf("Left\r\n");}else if(((XValue >= 4000) && (XValue <= 4100)) && ((YValue >= 2000) && (YValue <= 2200))){printf("Right\r\n");}else if(((XValue >= 2000) && (XValue <= 2200)) && ((YValue >= 4000) && (YValue <= 4100))){printf("Front\r\n");}else if(((XValue >= 2000) && (XValue <= 2200)) && ((YValue >= 0) && (YValue <= 10))){printf("Back\r\n");}vTaskDelay(1000);}
}
上述判断值为本人实际测试过的值,由于范围较小所以偏一点就可能检测失败,建议检测区间增加200左右用起来更舒服。方向也可以根据自己的实际需求取修改,我这里的方向可能不准,只是为了验证功能。
最后将摇杆任务加入任务创建函数。
static TaskHandle_t RockerBar_Handle = NULL;void RockerBar_Task(void * param);xTaskCreate((TaskFunction_t )RockerBar_Task, /* 任务入口函数 */(const char* )"RockerBar_Task",/* 任务名字 */(uint16_t )256, /* 任务栈大小 */(void* )NULL, /* 任务入口函数参数 */(UBaseType_t )2, /* 任务的优先级 */(TaskHandle_t* )&RockerBar_Handle);/* 任务控制块指针 */
到这里为止,摇杆按键就配置完成啦。接下来让我们看靠实际效果。
执行效果
效果非常完美!!
在下一章,我们将OLED加入这个项目中,敬请期待!