• 单片机操作系统,按键与FIFO


    前言

    1.之前做按键,在中断判断并进入回调函数,但是经常会导致其他任务来不及处理,或者是按键触发了但没有执行回调,即用户操作时感觉按键失灵。

    2.这里更新了一下代码,思路是这样的:中断进入按键扫描,有消抖,不阻塞,如果按键事件触发时即入列,然后操作系统每隔10ms进行一次轮询,若队列不为空,则出列并执行按键回调。

    有纰漏请指出,转载请说明。

    学习交流请发邮件 1280253714@qq.com

    key.c

    1. #include "includes.h"
    2. KEY_S key1;
    3. KEY_S key2;
    4. //按键平时为高电平,按下为低电平
    5. static void Key_GPIO_Config(void)
    6. {
    7. GPIO_InitTypeDef GPIO_InitStruct;
    8. RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK, ENABLE);
    9. RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK, ENABLE);
    10. GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    11. GPIO_InitStruct.GPIO_Pin = KEY1_GPIO_PIN;
    12. GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStruct);
    13. GPIO_InitStruct.GPIO_Pin = KEY2_GPIO_PIN;
    14. GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStruct);
    15. }
    16. void KeyOsInit(void)
    17. {
    18. Key_GPIO_Config();
    19. key1.keyNum = keyNum1;
    20. key1.state = keyNone;
    21. key1.pfnKeyCallBack = key1TaskProc;
    22. key2.keyNum = keyNum2;
    23. key2.state = keyNone;
    24. key2.pfnKeyCallBack = key2TaskProc;
    25. }
    26. static bool readKeyGpioLevel(u8 keyNum)
    27. {
    28. bool keyRes = KEY_OFF;
    29. switch ( keyNum ){
    30. case keyNum1:
    31. keyRes = GPIO_ReadInputDataBit(KEY1_GPIO_PORT, KEY1_GPIO_PIN);
    32. break;
    33. case keyNum2:
    34. keyRes = GPIO_ReadInputDataBit(KEY2_GPIO_PORT, KEY2_GPIO_PIN);
    35. break;
    36. default:
    37. break;
    38. }
    39. return keyRes;
    40. }
    41. void keyScanLoop(KEY_S *keyx)
    42. {
    43. if ( keyx->state == keyLong ){
    44. if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
    45. stKeyData.keyNum = keyx->keyNum;
    46. stKeyData.keyState = keyLong;
    47. KeyEnQueue(&stKeyQueue,&stKeyData);
    48. // keyx->pfnKeyCallBack(); //如果是执行这里的话,那么就是在中断进入回调了
    49. return;
    50. } else {
    51. keyx->preState = keyx->state;
    52. keyx->state = keyNone;
    53. }
    54. }
    55. // 不在检测状态时按键按下
    56. if ( 0 == keyx->bIsChecking ){
    57. if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
    58. keyx->readKeyLevel = KEY_ON;
    59. keyx->bIsChecking = 1;
    60. keyx->keyDownTimes = 0;
    61. keyx->preState = keyx->state;
    62. keyx->state = keyNone;
    63. keyx->timedReadRes = 0;
    64. keyx->checkInterval = checkIntervalMs;
    65. keyx->shakeInterval = shakeIntervalMs;
    66. }
    67. } else {
    68. // 按键周期倒计时
    69. if ( keyx->checkInterval ){
    70. keyx->checkInterval--;
    71. // 定时读取
    72. if ( keyx->timedReadInterval ){
    73. keyx->timedReadInterval--;
    74. } else {
    75. keyx->timedReadInterval = timedReadIntervalMs;
    76. if( KEY_ON == readKeyGpioLevel(keyx->keyNum) ){
    77. keyx->timedReadRes++;
    78. }
    79. }
    80. // 检测状态时按键按下
    81. if ( KEY_ON == keyx->readKeyLevel ) {
    82. // 按键软件消抖
    83. if ( keyx->shakeInterval ){
    84. keyx->shakeInterval--;
    85. } else {
    86. keyx->shakeInterval = shakeIntervalMs;
    87. if (KEY_ON == readKeyGpioLevel(keyx->keyNum)){
    88. keyx->keyDownTimes++;
    89. // 读取的电平反转
    90. keyx->readKeyLevel = KEY_OFF;
    91. }
    92. }
    93. } else {
    94. if (KEY_OFF == readKeyGpioLevel(keyx->keyNum)){
    95. keyx->readKeyLevel = KEY_ON;
    96. }
    97. }
    98. }
    99. // 按键倒计时结束,通过按下次数和定时读取次数判断按键键值
    100. else {
    101. keyx->bIsChecking = 0;
    102. switch (keyx->keyDownTimes){
    103. case keyNone:
    104. keyx->state = keyNone;
    105. return;
    106. case keyShort:
    107. keyx->state = keyShort;
    108. break;
    109. case keyDouble:
    110. case keyTriple: //按下三次也算双击
    111. keyx->state = keyDouble;
    112. break;
    113. default :
    114. keyx->state = keyLong;
    115. break;
    116. }
    117. if ( keyx->timedReadRes > keyLongTimes ){ //可自定义读取次数
    118. keyx->state = keyLong;
    119. }
    120. stKeyData.keyNum = keyx->keyNum;
    121. stKeyData.keyState = keyx->state;
    122. KeyEnQueue(&stKeyQueue,&stKeyData);
    123. // keyx->pfnKeyCallBack(); //如果是执行这里的话,那么就是在中断进入回调了
    124. }
    125. }
    126. }
    127. void KeyTaskProc(void)
    128. {
    129. if(stKeyQueue.state != KeyQueueEmpty)
    130. {
    131. if(KeyDeQueue(&stKeyQueue) != KeyDataEmpty)
    132. {
    133. stKeyData = stKeyQueue.dataDeQueue;
    134. switch(stKeyData.keyNum)
    135. {
    136. case keyNum1:
    137. key1TaskProc();
    138. break;
    139. case keyNum2:
    140. key2TaskProc();
    141. break;
    142. }
    143. }
    144. }
    145. }
    146. //定时器设置为1ms进一次中断
    147. void TIM3_IRQHandler()
    148. {
    149. if ( TIM_GetITStatus( TIM3, TIM_IT_Update) != RESET )
    150. {
    151. keyScanLoop(&key1);
    152. keyScanLoop(&key2);
    153. TIM_ClearITPendingBit(TIM3 , TIM_FLAG_Update);
    154. }
    155. }
    156. u32 key1Cnt=0;
    157. u32 key2Cnt=0;
    158. void key1TaskProc(void)
    159. {
    160. if(key1.state == keyShort){
    161. key1Cnt++;
    162. }
    163. if(key1.state == keyLong){
    164. if( key1.keyLongExeInterval ){
    165. key1.keyLongExeInterval--;
    166. } else {
    167. key1.keyLongExeInterval = keyLongExeIntervalMs;
    168. key1Cnt++;
    169. }
    170. }
    171. }
    172. void key2TaskProc(void)
    173. {
    174. if(key2.state == keyShort){
    175. key2Cnt++;
    176. }
    177. if(key2.state == keyLong){
    178. if( key2.keyLongExeInterval ){
    179. key2.keyLongExeInterval--;
    180. } else {
    181. key2.keyLongExeInterval = keyLongExeIntervalMs;
    182. key2Cnt++;
    183. }
    184. }
    185. }
    186. /**************************************** 按键队列处理 ****************************************/
    187. KeyQueue_S stKeyQueue;
    188. KeyQueueData_S stKeyData;
    189. /********************************************
    190. * @函数名 QueueInit
    191. * @描述 队列初始化
    192. * @参数 需要初始化的队列
    193. * @返回值 无
    194. * @注意 无
    195. ********************************************/
    196. void KeyQueueInit(KeyQueue_S *queue)
    197. {
    198. memset(&queue, 0, sizeof(queue));
    199. }
    200. /********************************************
    201. * @函数名 QueueStateDetermine
    202. * @描述 检查队列当前状态
    203. * @参数 需要检查的队列
    204. * @返回值 队列当前状态
    205. * @注意 无
    206. ********************************************/
    207. KeyQueueState KeyQueueStateDetermine(KeyQueue_S *queue)
    208. {
    209. if(queue->size == 0)
    210. {
    211. return KeyQueueEmpty;
    212. }
    213. else if(queue->size == KeyQueueMaxSize)
    214. {
    215. return KeyQueueFull;
    216. }
    217. else
    218. {
    219. return KeyQueueNormal;
    220. }
    221. }
    222. /********************************************
    223. * @函数名 EnQueue
    224. * @描述 入列
    225. * @参数
    226. queue 有入列需要的队列
    227. data 需要入列的数据
    228. * @返回值 无
    229. * @注意 当队列满时无法入列
    230. ********************************************/
    231. void KeyEnQueue(KeyQueue_S *queue, KeyQueueData_S *data)
    232. {
    233. if(queue->size == KeyQueueMaxSize)
    234. {
    235. return;
    236. }
    237. queue->dataEnQueue = *data;
    238. queue->data[queue->rear] = queue->dataEnQueue;
    239. queue->size++;
    240. queue->rear++;
    241. if(queue->rear == KeyQueueMaxSize)
    242. {
    243. queue->rear = 0;
    244. }
    245. queue->state = KeyQueueStateDetermine(queue);
    246. }
    247. /********************************************
    248. * @函数名 DeQueue
    249. * @描述 出列
    250. * @参数 queue 有出列需要的队列
    251. * @返回值 返回的数据是否为空
    252. * @注意 实际出列的数据放在 queue->dataDeQueue
    253. ********************************************/
    254. KeyDeQueueState KeyDeQueue(KeyQueue_S *queue)
    255. {
    256. if(queue->size == 0)
    257. {
    258. return KeyDataEmpty;
    259. }
    260. queue->dataDeQueue = queue->data[queue->front];
    261. memset(&queue->data[queue->front], 0, sizeof(queue->data[queue->front]));
    262. queue->size--;
    263. queue->front++;
    264. if(queue->front == KeyQueueMaxSize)
    265. {
    266. queue->front = 0;
    267. }
    268. queue->state = KeyQueueStateDetermine(queue);
    269. return KeyDataNormal;
    270. }

    key.h

    1. #ifndef __KEY_H
    2. #define __KEY_H
    3. #include "includes.h"
    4. typedef enum {
    5. keyNum1,
    6. keyNum2,
    7. keySum,
    8. } keyOrder;
    9. typedef enum {
    10. keyNone = 0,
    11. keyShort,
    12. keyDouble,
    13. keyTriple,
    14. keyLong
    15. } keyState;
    16. typedef struct key_s {
    17. keyOrder keyNum;
    18. bool bIsChecking; //正在检测
    19. bool bTaskProcessing;
    20. bool readKeyLevel;
    21. keyState preState; //上一个按键状态
    22. keyState state;
    23. u8 timedReadInterval; //定时读取的间隔
    24. u8 keyDownTimes; //一个检测周期按下的次数
    25. u16 timedReadRes; //定时读取,如果在一个检测周期按下的次数为1或2,而每隔n ms读取的低电平次数大于某个值,可认为是长按
    26. u16 shakeInterval; //抖动多少时间后进行检测
    27. u16 checkInterval; //整个检测的持续时间
    28. u16 keyLongInterval; //长按后隔一段时间再检测,避免检测到短按
    29. u16 keyLongExeInterval;
    30. void (*pfnKeyCallBack)(void);
    31. } KEY_S;
    32. extern KEY_S key1;
    33. extern KEY_S key2;
    34. #define keyLongTimes 40
    35. #define timedReadIntervalMs 10
    36. #define shakeIntervalMs 10
    37. #define checkIntervalMs 500
    38. #define keyLongExeIntervalMs 10 //OS 10ms进一次,10次触发一次长按,故为100ms
    39. #define KEY_ON 1 //按键平时为低电平,按下为高电平
    40. #define KEY_OFF 0
    41. #define KEY1_GPIO_PIN GPIO_Pin_0
    42. #define KEY1_GPIO_PORT GPIOA
    43. #define KEY1_GPIO_CLK RCC_APB2Periph_GPIOA
    44. #define KEY2_GPIO_PIN GPIO_Pin_13
    45. #define KEY2_GPIO_PORT GPIOC
    46. #define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC
    47. void KeyOsInit(void);
    48. void keyScanLoop(KEY_S *keyx);
    49. void KeyTaskProc(void);
    50. void key1TaskProc(void);
    51. void key2TaskProc(void);
    52. /**************************************** 按键队列处理 ****************************************/
    53. #define KeyQueueMaxSize 5 //队列最大存放的数据个数
    54. typedef enum {
    55. KeyQueueEmpty, //队列为空
    56. KeyQueueNormal, //队列不为空
    57. KeyQueueFull, //队列已满
    58. } KeyQueueState; //队列当前状态
    59. typedef enum {
    60. KeyDataEmpty, //数据为空
    61. KeyDataNormal, //数据不为空
    62. } KeyDeQueueState; //出列的数据情况
    63. typedef struct {
    64. keyOrder keyNum;
    65. keyState keyState;
    66. } KeyQueueData_S; //队列存放的数据类型
    67. // 定义队列结构
    68. typedef struct {
    69. KeyQueueData_S data[KeyQueueMaxSize]; //队列缓存
    70. KeyQueueData_S dataEnQueue; //入列数据
    71. KeyQueueData_S dataDeQueue; //出列数据
    72. KeyQueueState state; //队列当前状态
    73. u8 front; //队头
    74. u8 rear; //队尾
    75. u8 size; //队列大小
    76. } KeyQueue_S;
    77. void KeyQueueInit(KeyQueue_S *queue);
    78. KeyQueueState KeyQueueStateDetermine(KeyQueue_S *queue);
    79. void KeyEnQueue(KeyQueue_S *queue, KeyQueueData_S *data);
    80. KeyDeQueueState KeyDeQueue(KeyQueue_S *queue);
    81. extern KeyQueue_S stKeyQueue;
    82. extern KeyQueueData_S stKeyData;
    83. #endif

    main.c

    1. #include "includes.h"
    2. int main(void)
    3. {
    4. TimerOsInit();
    5. LED_Init();
    6. UartInit();
    7. AdcOsInit();
    8. OS_TaskInit();
    9. SysTickInit();
    10. KeyOsInit();
    11. OS_TaskCreat(Task1, UaetSendHeartBeat, 100);
    12. OS_TaskCreat(Task2, KeyTaskProc, 10);
    13. while (1){
    14. AdcTask();
    15. UartLoopTask();
    16. testPro();
    17. ledLoopTask();
    18. OS_TaskRun();
    19. }
    20. }

    演示效果

    STM32单片机操作系统、按键与FIFO_哔哩哔哩_bilibili

    STM32单片机操作系统、按键与FIFO

  • 相关阅读:
    C++ ,VCPKG那些事
    Java 反射机制到底是什么?
    知识蒸馏和知识图谱相结合的大模型微调方案
    【pytorch】2.2 pytorch 自动求导、 Tensor 与 Autograd
    scipy 与 sympy 模块在数学建模中的应用
    C++ 4种智能指针的定义与使用——学习记录008
    系统架构设计:11 论湖仓一体架构及其应用
    git 新手教程
    算法设计与分析 SCAU19184 传球游戏
    目标检测算法——工业缺陷数据集汇总1(附下载链接)
  • 原文地址:https://blog.csdn.net/weixin_45817947/article/details/132940788