打印
[活动专区]

【AutoChips MCU 评测报告】+单路ADC检测多路按键

[复制链接]
625|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 qjp1988113 于 2020-10-20 13:35 编辑

下面开始调试AC781X开发板。官方提供的例程均未从最简单的单一外设着手,而是一些
小型的几个外设的复合的程序,添加了些趣味性。
其中GPIO里面的一个多路按键的例子很有意思,思路很不错。
硬件电路设计如下:

可以看出,按下不同的按键,ADC检测的电压值不一样,这样我们就可以区分是哪一个按键按下了。
在IO管脚紧张,而ADC通道又有剩余的情况下,是个不错的选择。官方的例程,文档啥的,其实做的还是不错的。
这个调试GPIO不同模式的例子。每种模式都写了个相应的c文件,然后通过不同的宏定义,来执行对应函数。

例程中的中文注释的很详细:



下面为gpio_mux.c文件:


/*************<include>****************/
#include "gpio.h"
#include "ac78xx_timer.h"
#include "ac78xx_adc.h"

#if (GPIO_SAMPLE_SEL == GPIO_MUX_SAMPLE)
/*************<macro>******************/


/*************<enum>*******************/

/*************<union>******************/


/*************<struct>*****************/


/*************<variable>***************/
uint8_t                         g_scanKeyTimeCnt = 0;
uint32_t                         g_getSampKeyVal  = 0;        
uint8_t                                g_getK3CurtSts = 0,g_getK4CurtSts = 0,g_getK5CurtSts = 0;


/*************<prototype>**************/
void TIM2_IRQnCallBack(uint8_t lparam);


/**
* @prototype GPIO_Mux_Sample(void)
*
* @param[in] void
* @return         void
*
* @brief           Judge action of the keys by GPIO and achieve LED toggle.
*                         通过GPIO识别按键动作,完成LED状态翻转.        
*/
void GPIO_Mux_Sample(void)
{
        GPIO_InitHardwr();
        ADC_InitHardwr();
        TIMER_InitHardwr();
        
        while(1)
        {
                GPIO_ScanKeyAction();
        }
}

/**
* @prototype GPIO_InitHardwr(void)
*
* @param[in] void
* @return         void
*
* @brief           Initalize GPIO module.
*                         初始化GPIO模块.
*/
void GPIO_InitHardwr(void)
{
        GPIO_SetDir(LED2_PIN, GPIO_OUTPUT);//设置LED引脚为输出
        GPIO_SetDir(LED3_PIN, GPIO_OUTPUT);
        
        LED2_ON;//默认上电都点亮
        LED3_ON;
}

/**
* @prototype GPIO_ScanKeyAction(void)
*
* @param[in] void
* @return         void
*
* @brief           Scan Key Action Priodly.
*                         扫描按键动作.
*/
void GPIO_ScanKeyAction(void)
{
        if (g_scanKeyTimeCnt > 9)//每10ms扫描一次按键
        {
                g_scanKeyTimeCnt = 0;
               
                g_getSampKeyVal = ADC_GetSampKeyVal();//获取ADC采样结果
               
                /*这里之所以需要记住按键状态,目的是准确识别用按键动作,避免按键一次,执行动作多次*/
                if ((g_getK5CurtSts == KEY_UNDOWN) && (g_getSampKeyVal < 900))//(4095 * (560 / 2560))),K5按下,只有K5按下后松开,记为一次按键动作
                {
                        LED2_OFF;
                        LED3_OFF;
                        g_getK5CurtSts = KEY_DOWN;//记住按键按下状态
                }
                else if ((g_getK4CurtSts == KEY_UNDOWN) && (g_getSampKeyVal < 2100) && (g_getSampKeyVal > 900))//(4095 * (2060 / 4060)))
                {
                        LED3_TOGGLE;
                        g_getK4CurtSts = KEY_DOWN;//记住按键按下状态
                }
                else if ((g_getK3CurtSts == KEY_UNDOWN) && (g_getSampKeyVal < 3200) && (g_getSampKeyVal > 2100))//(4095 * (6760 / 8760)))
                {
                        LED2_TOGGLE;
                        g_getK3CurtSts = KEY_DOWN;//记住按键按下状态
                }
                else if (g_getSampKeyVal == 4095)//按键松开
                {
                        g_getK3CurtSts = KEY_UNDOWN;//恢复按键状态为未按下状态
                        g_getK4CurtSts = KEY_UNDOWN;
                        g_getK5CurtSts = KEY_UNDOWN;
                }
        }
}

/**
* @prototype TIMR_InitHardwr(void)
*
* @param[in] void
* @return         void
*
* @brief           Initalize TIM2 module.
*                         初始化TIM2模块.
*/
void TIMER_InitHardwr(void)
{
        TIMER_ConfigType timerConfig = {0};
        
        timerConfig.loadValue = (48000000 / (1000 - 1));//定时器时钟只能是外设总线时钟
        timerConfig.linkMode = 0;//不使用多定时器进行链接
        timerConfig.interruptEn = 1;//使能中断
        timerConfig.timerEn = 1;//使能定时器
        
        TIMER_SetCallback(TIMER2,TIM2_IRQnCallBack);//设置中断回调函数
        TIMER_Init(TIMER2,&timerConfig);
}

/**
* @prototype TIM2_IRQnCallBack(uint8_t lparam)
*
* @param[in] ...
* @return         ...
*
* @brief           TIM2 module interrupt handler.
*                         TIM2中断处理函数.
*/
void TIM2_IRQnCallBack(uint8_t lparam)
{
        if (TIMER_GetIntFlag(TIMER2))
    {
        TIMER_ClrIntFlag(TIMER2);
               
                if (g_scanKeyTimeCnt < 0xFF)
                {
                        g_scanKeyTimeCnt++;
                }
        }
}

/**
* @prototype ADC_InitHardwr(void)
*
* @param[in] void
* @return         void
*
* @brief           Initialize ADC module and corresponding pin.
*                         初始化ADC模块以及对应引脚.
*/
void ADC_InitHardwr(void)
{
        ADC_InitType  adcConfig = {0};
        ADC_TrigSourceType adcTrigSrc = {0};
        
        GPIO_SetFunc(K3_4_5_PIN, GPIO_FUNC_1);//设置引脚复用,复用功能1,ADC_IN1
        
        adcConfig.dataAlign = ADC_DataAlign_Right;//数据右对齐
        adcConfig.scanMode = ADC_Regular_Single_One_Mode;//一次只转换一个通道
        ADC_Init(ADC, &adcConfig);
        
        adcTrigSrc.regularTrigSource = ADC_TrigSource_Internal;//选择内部触发
        ADC_SetClockPrescaler(ADC, ADC_ADIV_DIVIDE_8);//设置分频系数,48M/8=6Mhz
        ADC_TrigSourceInit(ADC, &adcTrigSrc);
        
        ADC_SetRegularGroupLength(ADC, 1);//规则组,通道总数只有1个
        ADC_SetRegularGroupSequence(ADC, 1, ADC_CHANNEL_AD1);//选择转换通道
        ADC_ChannelSampleTimeSel(ADC, ADC_CHANNEL_AD1, ADC_SampleTime_14Cycle);//设置通道采样时间
        
        ADC_Cmd(ADC, ENABLE);
}

/**
* @prototype ADC_GetSampKeyVal(void)
*
* @param[in] void
* @return         Sample AD value of the key3/4/5 voltage.
*
* @brief           Sample key pin voltage.
*                         采样按键引脚电压.
*/
uint32_t ADC_GetSampKeyVal(void)
{
        ADC_SoftwareStartRegularConvCmd(ADC, ENABLE);//软件触发转换
        while(!ADC_GetIntFlag(ADC, ADC_FLAG_EOC));//等待转换完成
               
        return (ADC_GetRegularConversionValue(ADC));//获取结果
}

#endif
/*************<end>********************/

程序很简单,还是传统的定义结构体的写法,各种参数定义在结构体里面。只需配置流程正确,一般很难出错。
这里面GPIO,TIMER.ADC都写在一个文件里面了,如果独立分开,那么感觉结构就更清晰了。
程序如何运作的,文件里面标注的中文注释解释的已经很清楚了。这里给杰发赞一个,全篇中文注释。都国产了,还不发挥本土的语言优势?
虽然才调了第二个例子,芯片的参考手册也还没看,但我觉得上手应该很容易。
本例子中:TIMER做延时,while大循环里面,每隔10ms检测一次ADC采样通道引脚电压有无变化,
如果有变化,判断采样值在什么范围,来确定哪一个引脚,然后再触发相应引脚对应的任务。
IO引脚控制用了位段(Bit bond)控制的操作方式,使程序看起来简洁多了。

调试下载,现象如下:



使用特权

评论回复

相关帖子

沙发
czq2317| | 2020-10-20 14:32 | 只看该作者
这里只能检测一个按键按下,比如K5按下的时候,就不能检测到K3/K4按下了。把分压电阻改为并联,经过按键后到地,可实现多按键检测

使用特权

评论回复
板凳
正圆椭圆| | 2020-10-20 21:30 | 只看该作者
这个例程写得挺详细 谢谢分享

使用特权

评论回复
地板
marginer| | 2020-11-3 18:04 | 只看该作者
不错

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

111

主题

627

帖子

2

粉丝