在一个嵌入式系统中,可能存在许多输入或输出的IO口,输入有霍尔传感器、红外对管等,输出有LED、电源控制开关等。如果说硬件可以一次成型,那么随便一份代码都可以完成IO的配置工作,但研发阶段的产品,硬件各种修改是难免的,每一次 IO 的修改,对于底层开发人员来说,可能都是一次挑战。因为一旦有某一个 IO 配置错误,或者原来的配置没有修改正确(比如一个 IO 在原来的硬件适配中是输入,之后的硬件需要修改成输出),那么你很难查出来这是什么问题,因为这个时候不仅硬件修改了,软件也修改了,你需要先定位到底是软件问题还是硬件问题,所以一个好用的 IO 的配置框架就显得很有必要了。
有道友会说,不如使用 CubeMx 软件进行开发吧。
1、这个软件适用于 ST 单片机,以前还能用,现在,除非你家里有矿,不然谁用的起STM32?基本上都国产化了(虽然有些单片机号称兼容,但到底还是有些差异的)。2、公司原本的代码就是使用标准库,只是因为IO 的变化,你就需要把整个库换掉吗?时间上允许吗?你确定修改后不会出现大问题?3、国产化的芯片可没有所谓的标准库和HAL库供你选择,每一家都有各自的库,如果你的产品临时换方案怎么办?4、HAL 效率问题。今天鱼鹰介绍一个简单实用的框架,可用于快速增加或修改IO配置,甚至修改底层库。假设有3个 LED 作为输出、3 个霍尔传感器作为输入:输入配置代码:调试的时候,我们可以很方便的查看每个 IO 的状态是怎样的,而不用管 0 或 1 到底代表什么意思:#defineGPIOx_DefGPIO_TypeDef*
#define GPIOMode_Def GPIOMode_TypeDef
typedef struct
{
GPIOx_Def gpio;
uint16_t msk;
GPIOMode_Def pull_up_down;
} bsp_input_pin_def;
#define _GPIO_PIN_INPUT(id, pull, gpiox, pinx) [id].gpio = (GPIOx_Def)gpiox, [id].msk = (1 << pinx), [id].pull_up_down = (GPIOMode_Def)pull
#define GPIO_PIN_INPUT(id, pull, gpiox, pinx) _GPIO_PIN_INPUT(id, pull, gpiox, pinx)
#define bsp_pin_get_port(gpiox) ((uint16_t)((GPIO_TypeDef *)gpiox)->IDR)
#define bsp_pin_get_value(variable,id) do{ bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk ? variable |= (1 << id) : 0;} while(0)
typedef enum
{
PIN_INPUT_HALL_0 = 0, // 输入 IO 定义
PIN_INPUT_HALL_1,
PIN_INPUT_HALL_2,
PIN_INPUT_MAX
}bsp_pin_input_id_def;
static const bsp_input_pin_def bsp_input_pin [PIN_INPUT_MAX] =
{
GPIO_PIN_INPUT(PIN_INPUT_HALL_0, BSP_GPIO_PUPD_NONE, GPIOA, 0),
GPIO_PIN_INPUT(PIN_INPUT_HALL_1, BSP_GPIO_PUPD_NONE, GPIOB, 8),
GPIO_PIN_INPUT(PIN_INPUT_HALL_2, BSP_GPIO_PUPD_NONE, GPIOE, 9),
};
// 单个 IO 初始化函数
void bsp_pin_init_input(GPIOx_Def gpiox, uint32_t msk, GPIOMode_TypeDef pull_up_down)
{
uint32_t temp;
assert_param((msk & 0xffff0000) == 0 && gpiox != 0);
temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);
/* enable the led clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = (GPIOMode_Def)pull_up_down;
GPIO_InitStruct.GPIO_Pin = msk;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);
}
// 所有 IO 初始化
void gpio_input_init()
{
bsp_input_pin_def *info;
info = (bsp_input_pin_def *)&bsp_input_pin;
for(int i = 0; i < sizeof(bsp_input_pin)/sizeof(bsp_input_pin[0]); i++)
{
bsp_pin_init_input(info->gpio, info->msk, info->pull_up_down);
info++;
}
}
// 最多支持 32 个 IO 输入
uint32_t bsp_input_all(void)
{
uint32_t temp = 0;
bsp_pin_get_value(temp, PIN_INPUT_HALL_0);
bsp_pin_get_value(temp, PIN_INPUT_HALL_1);
bsp_pin_get_value(temp, PIN_INPUT_HALL_2);
return temp;
}
// 读取单个 IO 状态
uint32_t bsp_input_level(bsp_pin_input_id_def id)
{
return (bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk) ? 1 : 0;
}
typedef enum
{
HW_HAL_LEVEL_ACTIVE = 0, // 可直接修改为 0 或 1,另一个枚举值自动修改为相反值
HW_HAL_LEVEL_NO_ACTIVE = !HW_HAL_LEVEL_ACTIVE,
}hw_input_hal_status_def;
typedef struct
{
hw_input_hal_status_def hal_level0;
uint8_t hal_level1;
uint8_t hal_level2;
}bsp_input_status_def;
bsp_input_status_def bsp_input_status;
int main(void)
{
USRAT_Init(9600);//必须,进入调试模式后点击全速运行
gpio_input_init();
while(1)
{
uint32_t temp = bsp_input_all();
bsp_input_status.hal_level0 = (hw_input_hal_status_def)((temp >> PIN_INPUT_HALL_0) & 1);
bsp_input_status.hal_level1 = ((temp >> PIN_INPUT_HALL_1) & 1);
bsp_input_status.hal_level2 = ((temp >> PIN_INPUT_HALL_2) & 1);
}
}
![7f08e036-d57a-11ec-bce3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/95/5C/wKgZomTm_o-AGdlQAADHamPLefs083.png)
这个框架有啥好处呢?1、自动完成 GPIO 的时钟初始化工作,也就是说你只需要修改引脚即可,不必关心时钟配置,但对于特殊引脚(比如PB3),还是得另外配置才行。2、应用和底层具体 IO 分离,这样一旦修改了 IO,应用代码不需要进行任何修改。3、增加或删减 IO 变得很简单,增加 IO时,首先加入对应枚举,然后就可以添加对应的 IO 了。删除 IO时,只要屏蔽对应枚举值和引脚即可。4、参数检查功能, IO 删除时,因为屏蔽了对应的枚举,所以编译时可以帮你发现问题,而增加 IO 时,它可以帮你在运行时检查该 IO是否进行配置了,可以防止因为失误导致的问题。#define GPIOx_Def GPIO_TypeDef*
#define GPIOMode_Def GPIOMode_TypeDef
typedef struct
{
GPIOx_Def gpio;
uint32_t msk;
uint32_t init_value;
} bsp_output_pin_def;
#define _GPIO_PIN_OUT(id, gpiox, pinx, init) [id].gpio = gpiox, [id].msk = (1 << pinx), [id].init_value = init
#define GPIO_PIN_OUT(id, gpiox, pinx, init) _GPIO_PIN_OUT(id, gpiox, pinx, init)
#define _bsp_pin_output_set(gpiox, pin) (gpiox)->BSRR = pin
#define bsp_pin_output_set(gpiox, pin) _bsp_pin_output_set(gpiox, pin)
#define _bsp_pin_output_clr(gpiox, pin) (gpiox)->BRR = pin
#define bsp_pin_output_clr(gpiox, pin) _bsp_pin_output_clr(gpiox, pin)
typedef enum
{
PIN_OUTPUT_LED_G,
PIN_OUTPUT_LED_R,
PIN_OUTPUT_LED_B,
PIN_OUTPUT_MAX
}bsp_pin_output_id_def;
static const bsp_output_pin_def bsp_output_pin [PIN_OUTPUT_MAX] =
{
GPIO_PIN_OUT(PIN_OUTPUT_LED_G, GPIOA, 0, 0),
GPIO_PIN_OUT(PIN_OUTPUT_LED_R, GPIOF, 15, 0),
GPIO_PIN_OUT(PIN_OUTPUT_LED_B, GPIOD, 10, 0),
};
void bsp_pin_init_output(GPIOx_Def gpiox, uint32_t msk, uint32_t init)
{
uint32_t temp;
assert_param((msk & 0xffff0000) == 0 && gpiox != 0);
temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);
/* enable the led clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = (GPIOMode_Def)GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = msk;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);
if(init == 0)
{
bsp_pin_output_clr(gpiox, msk);
}
else
{
bsp_pin_output_set(gpiox, msk);
}
}
void bsp_output_init()
{
bsp_output_pin_def *info;
info = (bsp_output_pin_def *)&bsp_output_pin;
for(int i = 0; i < sizeof(bsp_output_pin)/sizeof(bsp_output_pin[0]); i++)
{
bsp_pin_init_output(info->gpio, info->msk, info->init_value);
info++;
}
}
void bsp_output(bsp_pin_output_id_def id, uint32_t value)
{
assert_param(id < PIN_OUTPUT_MAX);
if(value == 0)
{
bsp_pin_output_clr(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);
}
else
{
bsp_pin_output_set(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);
}
}
int main(void)
{
USRAT_Init(9600);//必须,进入调试模式后点击全速运行
bsp_output_init();
while(1)
{
bsp_output(PIN_OUTPUT_LED_G, 1);
bsp_output(PIN_OUTPUT_LED_B, 0);
bsp_output(PIN_OUTPUT_LED_R, 1);
}
}
![7f58713c-d57a-11ec-bce3-dac502259ad0.png](https://file1.elecfans.com//web2/M00/95/5C/wKgZomTm_o-AFowUAAApkxhOAXk077.png)
审核编辑 :李倩
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
单片机
+关注
关注
6011文章
44153浏览量
624369 -
霍尔传感器
+关注
关注
26文章
668浏览量
62533
原文标题:简单实用IO输入输出框架
文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
暖通控制系统可用钡铼分布式IO系统BL207
可编程逻辑控制功能,可替代DDC部分功能。 BACnet/IP IO系统BL207提供双网口支持交换机级联功能,支持标准的BACnet/IP协议,也可用于接入BACnet/IP控制器,可适配
![暖通控制系统<b class='flag-5'>可用</b>钡铼分布式<b class='flag-5'>IO</b>系统BL207](https://file1.elecfans.com//web2/M00/E9/AF/wKgaomZQQ0SACpFxAAb3WYs8_YM665.jpg)
自制测试框架(设置界面密码1)
(设置界面密码为1)
目前支持的指令如下:
1.没有任何编程基础的人快速实现简易自动化制作;
2.测试后可以直接把数据和结果直接写入到MES;
3.可持续增加新的功能及模块;
4.快速实现点
发表于 03-02 19:33
springboot框架介绍
Spring Boot 是一个开源的、用于开发微服务的框架,它基于 Java 平台。它提供了一种快速、敏捷的方式来构建独立的、可部署的、生产级别的 Spring 应用程序。Spring Boot
linux修改网卡ip配置文件
Linux是一种开源的操作系统,因此,它给用户提供了很高的自由度,可以根据个人需要进行各种定制和配置。其中,修改网络接口配置文件是常见的操作,可以通过修改网卡ip
异步IO框架iouring介绍
前言 Linux内核5.1支持了新的异步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe开发,意在提供一套公用的网络和磁盘异步IO,不过
![异步<b class='flag-5'>IO</b><b class='flag-5'>框架</b>iouring介绍](https://file1.elecfans.com/web2/M00/AE/D8/wKgZomVLOsOARiVrAABTDvMGONw229.jpg)
linux异步io框架iouring应用
Linux内核5.1支持了新的异步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe开发,意在提供一套公用的网络和磁盘异步IO,不过
![linux异步<b class='flag-5'>io</b><b class='flag-5'>框架</b>iouring应用](https://file1.elecfans.com/web2/M00/AE/D8/wKgZomVLOsOARiVrAABTDvMGONw229.jpg)
PyTorch IO DataPipes可用性、性能和功能
培训大型深层学习模式需要大型数据集。亚马逊简单存储服务(Amazon S3)是用于储存大型培训数据集的可缩放云点存储服务。 机器学习(ML)实践者需要高效的数据管道,能够从亚马逊S3下载数据,转换
![PyTorch <b class='flag-5'>IO</b> DataPipes<b class='flag-5'>可用</b>性、性能和功能](https://file1.elecfans.com/web2/M00/A2/65/wKgaomTv91uAcsskAADGcELXm6k332.png)
IO扩展模块有哪些特点
太网、串口等,使其能够与各种控制系统无缝集成。此外,IO扩展模块的配置也相对简单,工程师可以通过软件界面进行设定,从而快速适应不同的生产需求。 高度的可扩展性
发表于 08-31 18:14
安全风险分析-Arm A配置文件的Arm固件框架
本文档描述并模拟了影响Arm A配置文件规范的安全风险分析-Arm固件框架的威胁。在典型产品生命周期的不同阶段,如需求、设计、编码、测试和发布阶段,消除缺陷的成本会迅速增加。此外,修复体系结构规范中
发表于 08-09 07:26
AIX操作系统安装及配置
2.5.1 时区时间配置 2.5.2 系统参数设置 2.5.3 设置dump大小 2.5.4 修改磁盘定额 2.5.5 rootvg镜像 2.5.6 配置TCP/IP 2.5.7
发表于 07-19 09:38
•1次下载
评论