当前位置:网站首页>2、GPIO相关操作
2、GPIO相关操作
2022-07-06 03:20:00 【rou252051452】
1、概述
STM32的GPIO操作归类到RTT的pin设备中,本文参考RTT关于PIN设备的相关说明(RT-Thread 文档中心)进行编写及测试。共涉及到如下几个方面
- GPIO引脚的获取
- GPIO输出控制
- GPIO输入获取
- GPIO的外部中断实现
2、引脚编号的获取
RT-Thread 提供的引脚编号需要和芯片的引脚号区分开来,它们并不是同一个概念,引脚编号由 PIN 设备驱动程序定义,和具体的芯片相关。官方提供了3种方式可以获取引脚编号:
- 使用API
- 使用宏定义
- 查看驱动文件
2.1驱动文件更新
API获取方式通过函数rt_pin_get实现,程序如下
rt_base_t rt_pin_get(const char *name)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
RT_ASSERT(name[0] == 'P');
if(_hw_pin.ops->pin_get == RT_NULL)
{
return -RT_ENOSYS;
}
return _hw_pin.ops->pin_get(name);
}
查看初始化阶段对_hw_pin->ops的设置情况如下
const static struct rt_pin_ops _stm32_pin_ops =
{
stm32_pin_mode,
stm32_pin_write,
stm32_pin_read,
stm32_pin_attach_irq,
stm32_pin_dettach_irq,
stm32_pin_irq_enable,
};
struct rt_pin_ops
{
void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);
void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);
int (*pin_read)(struct rt_device *device, rt_base_t pin);
rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_int32_t pin,
rt_uint32_t mode, void (*hdr)(void *args), void *args);
rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_int32_t pin);
rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);
rt_base_t (*pin_get)(const char *name);
};
如上ops中并没有pin_get函数的实现方式,所以我们需要更新drv_gpio驱动文件。使用C:\RT-ThreadStudio\repo\Extract\Board_Support_Packages\RealThread\STM32H750-RT-ART-Pi\1.2.1\libraries\drivers的drv_gpio.c替换目录下得驱动文件,同步更新.h文件。查看更新后内容如下
const static struct rt_pin_ops _stm32_pin_ops =
{
stm32_pin_mode,
stm32_pin_write,
stm32_pin_read,
stm32_pin_attach_irq,
stm32_pin_dettach_irq,
stm32_pin_irq_enable,
stm32_pin_get,
};
2.2实现方式
2.2.1使用API
static rt_uint32_t reset_pin = 0;
reset_pin = rt_pin_get("PF.9");
rt_base_t rt_pin_get(const char *name)
{
RT_ASSERT(_hw_pin.ops != RT_NULL);
RT_ASSERT(name[0] == 'P');
if(_hw_pin.ops->pin_get == RT_NULL)
{
return -RT_ENOSYS;
}
return _hw_pin.ops->pin_get(name);
}
如上所示,rt_pin_get首先对输入的字符串进行了严重是否为字母P开头,最终调用的函数为_hw_pin.ops->pin_get(name);内容如下:
static rt_base_t stm32_pin_get(const char *name)
{
rt_base_t pin = 0;
int hw_port_num, hw_pin_num = 0;
int i, name_len;
name_len = rt_strlen(name);
if ((name_len < 4) || (name_len >= 6)) //进行字符串长度验证PA.0 PA.15 最短4,最长5
{
return -RT_EINVAL;
}
if ((name[0] != 'P') || (name[2] != '.')) //字符串第一个必须为P 第三个必须为.
{
return -RT_EINVAL;
}
if ((name[1] >= 'A') && (name[1] <= 'Z')) //端口范围在A-Z
{
hw_port_num = (int)(name[1] - 'A'); //端口编号计算,A-Z转换为0-25
}
else
{
return -RT_EINVAL;
}
for (i = 3; i < name_len; i++) //根据字符串的第4个进行编号转换PA.0=0 PA.15 = 15
{
hw_pin_num *= 10;
hw_pin_num += name[i] - '0';
}
pin = PIN_NUM(hw_port_num, hw_pin_num); //进行最后引脚编号的处理
return pin;
}
最终调用宏定义如下来进行引脚编号的获取
#define PIN_NUM(port, no) (((((port) & 0xFu) << 4) | ((no) & 0xFu)))
测算结果如下表,后续IO以此类推
name | port | no | result | name | port | no | result |
PA.0 | 0 | 0 | 0 | PC.0 | 2 | 0 | 32 |
PA.1 | 0 | 1 | 1 | PC.1 | 2 | 1 | 33 |
PA.2 | 0 | 2 | 2 | PC.2 | 2 | 2 | 34 |
PA.3 | 0 | 3 | 3 | PC.3 | 2 | 3 | 35 |
PA.4 | 0 | 4 | 4 | PC.4 | 2 | 4 | 36 |
PA.5 | 0 | 5 | 5 | PC.5 | 2 | 5 | 37 |
PA.6 | 0 | 6 | 6 | PC.6 | 2 | 6 | 38 |
PA.7 | 0 | 7 | 7 | PC.7 | 2 | 7 | 39 |
PA.8 | 0 | 8 | 8 | PC.8 | 2 | 8 | 40 |
PA.9 | 0 | 9 | 9 | PC.9 | 2 | 9 | 41 |
PA.10 | 0 | 10 | 10 | PC.10 | 2 | 10 | 42 |
PA.11 | 0 | 11 | 11 | PC.11 | 2 | 11 | 43 |
PA.12 | 0 | 12 | 12 | PC.12 | 2 | 12 | 44 |
PA.13 | 0 | 13 | 13 | PC.13 | 2 | 13 | 45 |
PA.14 | 0 | 14 | 14 | PC.14 | 2 | 14 | 46 |
PA.15 | 0 | 15 | 15 | PC.15 | 2 | 15 | 47 |
PB.0 | 1 | 0 | 16 | PD.0 | 3 | 0 | 48 |
PB.1 | 1 | 1 | 17 | PD.1 | 3 | 1 | 49 |
PB.2 | 1 | 2 | 18 | PD.2 | 3 | 2 | 50 |
PB.3 | 1 | 3 | 19 | PD.3 | 3 | 3 | 51 |
PB.4 | 1 | 4 | 20 | PD.4 | 3 | 4 | 52 |
PB.5 | 1 | 5 | 21 | PD.5 | 3 | 5 | 53 |
PB.6 | 1 | 6 | 22 | PD.6 | 3 | 6 | 54 |
PB.7 | 1 | 7 | 23 | PD.7 | 3 | 7 | 55 |
PB.8 | 1 | 8 | 24 | PD.8 | 3 | 8 | 56 |
PB.9 | 1 | 9 | 25 | PD.9 | 3 | 9 | 57 |
PB.10 | 1 | 10 | 26 | PD.10 | 3 | 10 | 58 |
PB.11 | 1 | 11 | 27 | PD.11 | 3 | 11 | 59 |
PB.12 | 1 | 12 | 28 | PD.12 | 3 | 12 | 60 |
PB.13 | 1 | 13 | 29 | PD.13 | 3 | 13 | 61 |
PB.14 | 1 | 14 | 30 | PD.14 | 3 | 14 | 62 |
PB.15 | 1 | 15 | 31 | PD.15 | 3 | 15 | 63 |
2.2.2使用宏定义
#define LED0_PIN GET_PIN(F, 9)
#define __STM32_PORT(port) GPIO##port##_BASE
#if defined(SOC_SERIES_STM32MP1)
#define GET_PIN(PORTx,PIN) (GPIO##PORTx == GPIOZ) ? (176 + PIN) : ((rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x1000UL) )) + PIN))
#else
#define GET_PIN(PORTx,PIN) (rt_base_t)((16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) )) + PIN)
#endif
宏定义 | PORTx | 16 * ( ((rt_base_t)__STM32_PORT(PORTx) - (rt_base_t)GPIOA_BASE)/(0x0400UL) ) | PIN | result | |
GET_PIN(A, 0) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 1) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 2) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(A, 3) | GPIOA_BASE | D3_AHB1PERIPH_BASE + 0x0000UL | 0 | 0 | 0 |
GET_PIN(B, 0) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 0 | 16 |
GET_PIN(B, 1) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 1 | 17 |
GET_PIN(B, 2) | GPIOB_BASE | D3_AHB1PERIPH_BASE + 0x0400UL | 1 | 2 | 18 |
结果与API实现方式结果相同。
2.2.3查看驱动文件
该方式在更新驱动文件drv_gpio后没有提供官方文档说明的查看驱动方式,这里不进行详细说明。
3、GPIO输出控制
我们通过控制主控板上的LED灯来验证GPIO的输出功能。
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#define LED0 GET_PIN(B, 0)
void example_gpio()
{
rt_pin_mode(LED0,PIN_MODE_OUTPUT); //设置管脚为输出
rt_pin_write(LED0,PIN_LOW);
}
MSH_CMD_EXPORT(example_gpio, example_gpio)
4、GPIO输入控制
通过板载按键来控制核心板的LED灯亮灭,按下后灯亮,松开后灯灭
#include "rtthread.h"
#include "rtdevice.h"
#include "board.h"
#define LED0 GET_PIN(B, 0)
void example_gpio()
{
static rt_uint32_t key = 0;
rt_pin_mode(LED0,PIN_MODE_OUTPUT); //设置管脚为输出
rt_pin_write(LED0,PIN_LOW);
key = rt_pin_get("PA.0"); //获取引脚编号
rt_pin_mode(key,PIN_MODE_INPUT); //设置管脚为输入
while(1)
{
if(rt_pin_read(key) == PIN_HIGH) //读取输入状态
{
rt_pin_write(LED0,PIN_LOW);
}
else
{
rt_pin_write(LED0,PIN_HIGH);
}
rt_thread_mdelay(1000);
}
}
MSH_CMD_EXPORT(example_gpio, example_gpio)
5、GPIO中断
通过板载按键来控制核心板的LED灯亮灭,按下后为上升沿中断,灯亮。松开后为下降沿中断,灯灭
6、驱动过程详解
6.1模式设置
模式设置调用的函数为rt_pin_mode,最终调用的函数为ops->pin_mode,为drv_gpio.c中的stm32_pin_mode函数,具体内容如下
static void stm32_pin_mode(rt_device_t dev, rt_base_t pin, rt_base_t mode)
{
GPIO_InitTypeDef GPIO_InitStruct;
//识别端口号是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return;
}
/*
进行GPIO初始化结构体的相关赋值
默认配置为推挽输出,高速,无上下拉
*/
GPIO_InitStruct.Pin = PIN_STPIN(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;
}
/*
进行GPIO初始化,根据宏定义PIN_STPORT识别出GPIOx的地址
*/
HAL_GPIO_Init(PIN_STPORT(pin), &GPIO_InitStruct);
}
6.2输出电平控制
输出电平调用的函数为rt_pin_write,最终调用的函数为ops->pin_write,为drv_gpio.c中的stm32_pin_write函数,具体内容如下
static void stm32_pin_write(rt_device_t dev, rt_base_t pin, rt_base_t value)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
//根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输出
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
gpio_port = PIN_STPORT(pin);
gpio_pin = PIN_STPIN(pin);
HAL_GPIO_WritePin(gpio_port, gpio_pin, (GPIO_PinState)value);
}
}
6.3输入电平获取
输入电平获取调用的函数为rt_pin_read,最终调用的函数为ops->pin_read,为drv_gpio.c中的stm32_pin_read函数,具体内容如下
static int stm32_pin_read(rt_device_t dev, rt_base_t pin)
{
GPIO_TypeDef *gpio_port;
uint16_t gpio_pin;
int value = PIN_LOW;
//根据宏定义将引脚序号转换为port和pin信息,调用HAL库函数进行输入信息的获取
if (PIN_PORT(pin) < PIN_STPORT_MAX)
{
gpio_port = PIN_STPORT(pin);
gpio_pin = PIN_STPIN(pin);
value = HAL_GPIO_ReadPin(gpio_port, gpio_pin);
}
return value;
}
6.4绑定引脚中断回调函数
绑定中断函数调用的是rt_pin_attach_irq,最终调用的函数为ops->pin_attach_irq,为drv_gpio.c中的stm32_pin_attach_irq函数,具体内容如下
只进行了rtt下pin设备的中断管脚模式和中断回调函数的赋值,未进行实质性的STM32的底层HAL库配置。
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)
{
rt_base_t level;
rt_int32_t irqindex = -1;
//识别端口是否正确
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
//识别中断源为几 0-15
irqindex = bit2bitno(PIN_STPIN(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;
}
具体的HAL库底层驱动配置在rt_pin_irq_enable函数中实现,该函数最终调用函数为stm32_pin_irq_enable内容如下:
static rt_err_t stm32_pin_irq_enable(struct rt_device *device, rt_base_t pin,
rt_uint32_t enabled)
{
const struct pin_irq_map *irqmap;
rt_base_t level;
rt_int32_t irqindex = -1;
GPIO_InitTypeDef GPIO_InitStruct;
//识别IO是否合法
if (PIN_PORT(pin) >= PIN_STPORT_MAX)
{
return -RT_ENOSYS;
}
//使能中断,进行是能的配置。
if (enabled == PIN_IRQ_ENABLE)
{
//获取管脚序号
irqindex = bit2bitno(PIN_STPIN(pin));
if (irqindex < 0 || irqindex >= ITEM_NUM(pin_irq_map))
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
//pin_irq_hdr_tab的内容在stm32_pin_attach_irq时进行了赋值。
if (pin_irq_hdr_tab[irqindex].pin == -1)
{
rt_hw_interrupt_enable(level);
return RT_ENOSYS;
}
//根据管脚获取中断序号 EXTI0_IRQn-EXTI4_IRQn或EXTI9_5_IRQn
irqmap = &pin_irq_map[irqindex];
//配置GPIO初始化结构体
/* Configure GPIO_InitStructure */
GPIO_InitStruct.Pin = PIN_STPIN(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(PIN_STPORT(pin), &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(PIN_STPIN(pin));
if (irqmap == RT_NULL)
{
return RT_ENOSYS;
}
level = rt_hw_interrupt_disable();
//复位GPIO
HAL_GPIO_DeInit(PIN_STPORT(pin), PIN_STPIN(pin));
pin_irq_enable_mask &= ~irqmap->pinbit;
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);
}
rt_hw_interrupt_enable(level);
}
else
{
return -RT_ENOSYS;
}
return RT_EOK;
}
边栏推荐
- Research on cooperative control of industrial robots
- Recommended foreign websites for programmers to learn
- 建模规范:命名规范
- SD卡报错“error -110 whilst initialising SD card
- Descriptor implements ORM model
- 电机控制反Park变换和反Clarke变换公式推导
- Linear programming matlab
- resulttype和resultmap的区别和应用场景
- [concept] Web basic concept cognition
- pytorch加载数据
猜你喜欢
EDCircles: A real-time circle detector with a false detection control 翻译
mysqldump数据备份
svg拖动点裁剪图片js特效
Analyze 菜单分析
1.16 - 校验码
Tidb ecological tools (backup, migration, import / export) collation
SAP ALV颜色代码对应颜色(整理)
IPv6 jobs
The next industry outlet: NFT digital collection, is it an opportunity or a foam?
[unity3d] GUI control
随机推荐
Differences and application scenarios between resulttype and resultmap
Force buckle 1189 Maximum number of "balloons"
What is the investment value of iFLYTEK, which does not make money?
Detailed use of dbutils # yyds dry goods inventory #
BUUCTF刷题笔记——[极客大挑战 2019]EasySQL 1
Redis cache breakdown, cache penetration, cache avalanche
多态day02
Remote Sensing Image Super-resolution and Object Detection: Benchmark and State of the Art
1003 emergency (25 points), "DIJ deformation"
Pytorch基础——(2)张量(tensor)的数学运算
An article about liquid template engine
遥感图像超分辨重建综述
Audio audiorecord binder communication mechanism
Lua uses require to load the shared library successfully, but the return is Boolean (always true)
jsscript
SD卡報錯“error -110 whilst initialising SD card
ArabellaCPC 2019(补题)
Microsoft Research, UIUC & Google research | antagonistic training actor critic based on offline training reinforcement learning
Redis SDS principle
How to write compile scripts compatible with arm and x86 (Makefile, cmakelists.txt, shell script)