当前位置:网站首页>潘多拉 IOT 开发板学习(RT-Thread)—— 实验4 蜂鸣器+马达实验【按键外部中断】(学习笔记)
潘多拉 IOT 开发板学习(RT-Thread)—— 实验4 蜂鸣器+马达实验【按键外部中断】(学习笔记)
2022-07-05 00:41:00 【小辉_Super】
本文代码参考 RT-Thread 官方 BSP
文章目录
实验功能
例程源码:(main.c)
该实验实现的功能:4 个按键对应不同的功能(控制电机左转或右转,控制蜂鸣器响灭),其中 3 个按键是通过外部中断的方式检测的(另外的一个按键通过轮询检测),大部分操作代码在中断服务(回调)函数中,电机控制和蜂鸣器控制的代码很简单,全是 GPIO 写操作,就不单独分析了。
/* * Copyright (c) 2006-2018, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2018-08-23 balanceTWK first implementation */
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
enum
{
MOTOR_STOP,
MOTOR_LEFT,
MOTOR_RIGHT
};
/* 电机控制 */
void motor_ctrl(rt_uint8_t turn)
{
if (turn == MOTOR_STOP)
{
rt_pin_write(PIN_MOTOR_A, PIN_LOW);
rt_pin_write(PIN_MOTOR_B, PIN_LOW);
}
else if (turn == MOTOR_LEFT)
{
rt_pin_write(PIN_MOTOR_A, PIN_LOW);
rt_pin_write(PIN_MOTOR_B, PIN_HIGH);
}
else if (turn == MOTOR_RIGHT)
{
rt_pin_write(PIN_MOTOR_A, PIN_HIGH);
rt_pin_write(PIN_MOTOR_B, PIN_LOW);
}
else
{
LOG_D("err parameter ! Please enter 0-2.");
}
}
void beep_ctrl(rt_uint8_t on)
{
if (on)
{
rt_pin_write(PIN_BEEP, PIN_HIGH);
}
else
{
rt_pin_write(PIN_BEEP, PIN_LOW);
}
}
/* 中断回调 */
void irq_callback(void *args)
{
rt_uint32_t sign = (rt_uint32_t)args;
switch (sign)
{
case PIN_KEY0:
motor_ctrl(MOTOR_LEFT);
LOG_D("KEY0 interrupt. motor turn left.");
break;
case PIN_KEY1:
motor_ctrl(MOTOR_RIGHT);
LOG_D("KEY1 interrupt. motor turn right.");
break;
case PIN_KEY2:
motor_ctrl(MOTOR_STOP);
LOG_D("KEY2 interrupt. motor stop.");
break;
default:
LOG_E("error sign= %d !", sign);
break;
}
}
int main(void)
{
unsigned int count = 1;
/* 设置按键引脚为输入模式 */
rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(PIN_KEY1, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(PIN_KEY2, PIN_MODE_INPUT_PULLUP);
rt_pin_mode(PIN_WK_UP, PIN_MODE_INPUT_PULLDOWN);
/* 设置电机控制引脚为输入模式 */
rt_pin_mode(PIN_MOTOR_A, PIN_MODE_OUTPUT);
rt_pin_mode(PIN_MOTOR_B, PIN_MODE_OUTPUT);
/* 设置蜂鸣器引脚为输出模式 */
rt_pin_mode(PIN_BEEP, PIN_MODE_OUTPUT);
/* 设置按键中断模式与中断回调函数 */
rt_pin_attach_irq(PIN_KEY0, PIN_IRQ_MODE_FALLING, irq_callback, (void *)PIN_KEY0);
rt_pin_attach_irq(PIN_KEY1, PIN_IRQ_MODE_FALLING, irq_callback, (void *)PIN_KEY1);
rt_pin_attach_irq(PIN_KEY2, PIN_IRQ_MODE_FALLING, irq_callback, (void *)PIN_KEY2);
/* 使能中断 */
rt_pin_irq_enable(PIN_KEY0, PIN_IRQ_ENABLE);
rt_pin_irq_enable(PIN_KEY1, PIN_IRQ_ENABLE);
rt_pin_irq_enable(PIN_KEY2, PIN_IRQ_ENABLE);
while (count > 0)
{
if (rt_pin_read(PIN_WK_UP) == PIN_HIGH)
{
rt_thread_mdelay(50);
if (rt_pin_read(PIN_WK_UP) == PIN_HIGH)
{
LOG_D("WK_UP pressed. beep on.");
beep_ctrl(1);
}
}
else
{
beep_ctrl(0);
}
rt_thread_mdelay(10);
count++;
}
return 0;
}
代码剖析
rt_pin_mode()
该函数的作用是 GPIO Pin 的初始化,定义为
/* RT-Thread Hardware PIN APIs */
void rt_pin_mode(rt_base_t pin, rt_base_t mode)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
_hw_pin.ops->pin_mode(&_hw_pin.parent, pin, mode);
}
参数 pin 是一个 rt_base_t 变量(long),下面的 GET_PIN() 是 STM32 的 pin 值宏定义,第一个参数填大写字母,第二个参数填数字。
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA)/(0x0400UL) )) + PIN)
#define __STM32_PORT(port) GPIO##port // ## 是字符连接符,假如 port 为 A,则表示 GPIOA
例如实验中的
#define PIN_LED_R GET_PIN(E, 7),表示 GPIOE GPIO_Pin7
目前 RT-Thread 支持的引脚工作模式包括:
#define PIN_MODE_OUTPUT 0x00 /* 输出 */
#define PIN_MODE_INPUT 0x01 /* 输入 */
#define PIN_MODE_INPUT_PULLUP 0x02 /* 上拉输入 */
#define PIN_MODE_INPUT_PULLDOWN 0x03 /* 下拉输入 */
#define PIN_MODE_OUTPUT_OD 0x04 /* 开漏输出 */
在 bsp 的 drv_gpio.c 文件中,有底层 GPIO 驱动,下面是 STM32 的 GPIO 模式设置的驱动函数(大家应该很熟悉,就是用 HAL 库写的 GPIO 初始化代码)
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
const struct pin_index *index;
GPIO_InitTypeDef GPIO_InitStruct;
index = get_pin(pin);
if (index == RT_NULL)
{
return;
}
/* Configure GPIO_InitStructure */
GPIO_InitStruct.Pin = index->pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
if (mode == PIN_MODE_OUTPUT)
{
/* output setting */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT)
{
/* input setting: not pull. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
else if (mode == PIN_MODE_INPUT_PULLUP)
{
/* input setting: pull up. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
}
else if (mode == PIN_MODE_INPUT_PULLDOWN)
{
/* input setting: pull down. */
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
}
else if (mode == PIN_MODE_OUTPUT_OD)
{
/* output setting: od. */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
HAL_GPIO_Init(index->gpio, &GPIO_InitStruct);
}
rt_pin_attach_irq()
这是 RT-Thread 中断绑定(注册)函数,它会调用当前平台驱动中的相应函数。
rt_err_t rt_pin_attach_irq(rt_int32_t pin, rt_uint32_t mode,
void (*hdr)(void *args), void *args)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
if(_hw_pin.ops->pin_attach_irq)
{
return _hw_pin.ops->pin_attach_irq(&_hw_pin.parent, pin, mode, hdr, args);
}
return RT_ENOSYS;
}
bsp 驱动(drv_gpio.c)中定义了 STM32 的中断注册函数 stm32_pin_attach_irq():
static rt_err_t stm32_pin_attach_irq(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args)
{
const struct pin_index *index;
rt_base_t level;
rt_int32_t irqindex = -1;
index = get_pin(pin);
if (index == RT_NULL)
{
return RT_ENOSYS;
}
irqindex = bit2bitno(index->pin);
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == pin &&
pin_irq_hdr_tab[irqindex].hdr == hdr &&
pin_irq_hdr_tab[irqindex].mode == mode &&
pin_irq_hdr_tab[irqindex].args == args)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
if (pin_irq_hdr_tab[irqindex].pin != -1)
{
rt_hw_interrupt_enable(level);
return RT_EBUSY;
}
pin_irq_hdr_tab[irqindex].pin = pin;
pin_irq_hdr_tab[irqindex].hdr = hdr;
pin_irq_hdr_tab[irqindex].mode = mode;
pin_irq_hdr_tab[irqindex].args = args;
rt_hw_interrupt_enable(level);
return RT_EOK;
}
attach 函数的原理很简单,就是将当前中断信息存放到驱动代码中的一个中断表中,中断表的结构体定义为:
struct rt_pin_irq_hdr
{
rt_int16_t pin;
rt_uint16_t mode;
void (*hdr)(void *args); // 中断回调函数
void *args;
};
rt_pin_irq_enable()
这是 RT-Thread 内核的 pin 中断使能函数,实际操作的是平台驱动对应函数,
rt_err_t rt_pin_irq_enable(rt_base_t pin, rt_uint32_t enabled)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
if(_hw_pin.ops->pin_irq_enable)
{
return _hw_pin.ops->pin_irq_enable(&_hw_pin.parent, pin, enabled);
}
return RT_ENOSYS;
}
STM32 平台驱动中的中断使能函数如下,代码量还是很大的(HAL 库中外部中断的相关配置,由于要考虑不同参数选项,所以代码量大):
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
rt_uint32_t enabled)
{
const struct pin_index *index;
const struct pin_irq_map *irqmap;
rt_base_t level;
rt_int32_t irqindex = -1;
GPIO_InitTypeDef GPIO_InitStruct;
index = get_pin(pin);
if (index == RT_NULL)
{
return RT_ENOSYS;
}
if (enabled == PIN_IRQ_ENABLE)
{
irqindex = bit2bitno(index->pin);
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
if (pin_irq_hdr_tab[irqindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_ENOSYS;
}
irqmap = &pin_irq_map[irqindex];
/* Configure GPIO_InitStructure */
GPIO_InitStruct.Pin = index->pin;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
switch (pin_irq_hdr_tab[irqindex].mode)
{
case PIN_IRQ_MODE_RISING:
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
break;
case PIN_IRQ_MODE_FALLING:
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
break;
case PIN_IRQ_MODE_RISING_FALLING:
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
break;
}
HAL_GPIO_Init(index->gpio, &GPIO_InitStruct);
HAL_NVIC_SetPriority(irqmap->irqno, 5, 0);
HAL_NVIC_EnableIRQ(irqmap->irqno);
pin_irq_enable_mask |= irqmap->pinbit;
rt_hw_interrupt_enable(level);
}
else if (enabled == PIN_IRQ_DISABLE)
{
irqmap = get_pin_irq_map(index->pin);
if (irqmap == RT_NULL)
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
HAL_GPIO_DeInit(index->gpio, index->pin);
pin_irq_enable_mask &= ~irqmap->pinbit;
#if defined(SOC_SERIES_STM32F0) || defined(SOC_SERIES_STM32G0)
if (( irqmap->pinbit>=GPIO_PIN_0 )&&( irqmap->pinbit<=GPIO_PIN_1 ))
{
if(!(pin_irq_enable_mask&(GPIO_PIN_0|GPIO_PIN_1)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if (( irqmap->pinbit>=GPIO_PIN_2 )&&( irqmap->pinbit<=GPIO_PIN_3 ))
{
if(!(pin_irq_enable_mask&(GPIO_PIN_2|GPIO_PIN_3)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if (( irqmap->pinbit>=GPIO_PIN_4 )&&( irqmap->pinbit<=GPIO_PIN_15 ))
{
if(!(pin_irq_enable_mask&(GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|
GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
#else
if (( irqmap->pinbit>=GPIO_PIN_5 )&&( irqmap->pinbit<=GPIO_PIN_9 ))
{
if(!(pin_irq_enable_mask&(GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else if (( irqmap->pinbit>=GPIO_PIN_10 )&&( irqmap->pinbit<=GPIO_PIN_15 ))
{
if(!(pin_irq_enable_mask&(GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15)))
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
}
else
{
HAL_NVIC_DisableIRQ(irqmap->irqno);
}
#endif
rt_hw_interrupt_enable(level);
}
else
{
return -RT_ENOSYS;
}
return RT_EOK;
}
中断处理函数
中断处理函数已经在 main.c 中定义,这里不展示了。
当外部中断触发时,会触发 HAL库的中断函数 HAL_GPIO_EXTI_Callback(),而 STM32 bsp 中在该函数里运行了pin_irq_hdr(bit2bitno(GPIO_Pin));,该函数会根据中断注册时分配的中断号来调用相应的回调函数。
HAL_GPIO_EXTI_Callback()
#if defined(SOC_SERIES_STM32G0)
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
pin_irq_hdr(bit2bitno(GPIO_Pin));
}
void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
{
pin_irq_hdr(bit2bitno(GPIO_Pin));
}
#else
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
pin_irq_hdr(bit2bitno(GPIO_Pin));
}
#endif
pin_irq_hdr()
rt_inline void pin_irq_hdr(int irqno)
{
if (pin_irq_hdr_tab[irqno].hdr)
{
pin_irq_hdr_tab[irqno].hdr(pin_irq_hdr_tab[irqno].args);
}
}
rt_pin_read()
GPIO 读函数,下面是函数的定义:
int rt_pin_read(rt_base_t pin)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
return _hw_pin.ops->pin_read(&_hw_pin.parent, pin);
}
和 GPIO 模式配置函数类似,它会调用底层驱动里对应的函数,该底层函数是通过 HAL_GPIO_ReadPin() 来获取 GPIO 的电平。
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
int value;
const struct pin_index *index;
value = PIN_LOW;
index = get_pin(pin);
if (index == RT_NULL)
{
return value;
}
value = HAL_GPIO_ReadPin(index->gpio, index->pin);
return value;
}
rt_thread_mdelay()
这是 RT-Thread 的毫秒级延时函数,定义如下:
rt_err_t rt_thread_mdelay(rt_int32_t ms)
{
rt_tick_t tick;
// 获取需要的时钟节拍
tick = rt_tick_from_millisecond(ms);
// 阻塞相应的节拍时间
return rt_thread_sleep(tick);
}
rt_tick_from_millisecond()
/** * 算出 ms 对应的时钟节拍数 * * * @param ms the specified millisecond * - Negative Number wait forever * - Zero not wait * - Max 0x7fffffff * * @return the calculated tick */
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER; // -1
}
else
{
// 将“每秒节拍数” / 1000 * ms,算出对应的秒节拍数
tick = RT_TICK_PER_SECOND * (ms / 1000);
// 加上小于 1000ms 部分的节拍数
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
}
/* return the calculated tick */
return tick;
}
rt_thread_sleep()
线程睡眠(挂起)函数,参数是系统节拍数:
/** * 该函数能让当前线程挂起一段时间(由 tick 决定) * * @param tick the sleep ticks * * @return RT_EOK */
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
register rt_base_t temp;
struct rt_thread *thread;
/* set to current thread */
thread = rt_thread_self();
RT_ASSERT(thread != RT_NULL);
RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
/* disable interrupt */
temp = rt_hw_interrupt_disable();
/* suspend thread */
rt_thread_suspend(thread);
/* reset the timeout of thread timer and start it */
rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);
rt_timer_start(&(thread->thread_timer));
/* enable interrupt */
rt_hw_interrupt_enable(temp);
rt_schedule();
/* clear error number of this thread to RT_EOK */
if (thread->error == -RT_ETIMEOUT)
thread->error = RT_EOK;
return RT_EOK;
}
LOG_D()
本实验中,我们可以将 LOG_D() 视为 rt_kprintf(),
#define dbg_log_line(lvl, color_n, fmt, ...) \ do \ {
\ _DBG_LOG_HDR(lvl, color_n); \ rt_kprintf(fmt, ##__VA_ARGS__); \ _DBG_LOG_X_END; \ } \ while (0)
LOG_D 是 RT-Thread 内核里的一个日志打印函数,详情可见:《RT-Thread 文档中心——ulog 日志》
RT-Thread 的日志 API 包括:

rt_pin_write()
GPIO 写函数,下面是函数的定义,
void rt_pin_write(rt_base_t pin, rt_base_t value)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
_hw_pin.ops->pin_write(&_hw_pin.parent, pin, value);
}
和 GPIO 模式配置函数类似,它会调用底层驱动里对应的函数,该底层函数是通过 HAL_GPIO_WritePin() 来完成 GPIO Pin 的修改。
static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
const struct pin_index *index;
index = get_pin(pin);
if (index == RT_NULL)
{
return;
}
HAL_GPIO_WritePin(index->gpio, index->pin, (GPIO_PinState)value);
}
边栏推荐
- What you learned in the eleventh week
- OpenHarmony资源管理详解
- 有哪些收益稳定的理财产品,这两个都不错
- 大专学历,33岁宝妈又怎样?我照样销售转测试,月入13k+
- Les phénomènes de « salaire inversé » et de « remplacement des diplômés » indiquent que l'industrie des tests a...
- Some basic functions of enterprise projects are developed, and important things are saved to online first a
- Getting started with Paxos
- 【路径规划】RRT增加动力模型进行轨迹规划
- 4. Scala writes HelloWorld in idea, in-depth analysis of accompanying objects, and association of source packages
- 两个数相互替换
猜你喜欢
随机推荐
[circuit design] optocoupler use and circuit design summary
GDB common commands
What you learned in the eleventh week
Distributed base theory
Playwright之录制
What did I pay for it transfer to testing post from confusion to firmness?
IT转测试岗,从迷茫到坚定我究竟付出了什么?
Life is changeable, and the large intestine covers the small intestine. This time, I can really go home to see my daughter-in-law...
企业公司项目开发好一部分基础功能,重要的事保存到线上第一a
Detailed explanation of openharmony resource management
Design of emergency lighting evacuation indication system for urban rail transit station
大专学历,33岁宝妈又怎样?我照样销售转测试,月入13k+
Hisilicon 3559 universal platform construction: YUV422 pit stepping record
[Yocto RM]11 - Features
Daily practice (18): stack containing min function
Learning of basic amplification circuit
NPM install error forced installation
Huawei employs millions of data governance experts! The 100 billion market behind it deserves attention
Learn C language from scratch day 024
The difference between string STR and new string








