0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

给大家分享一个非常简单的IO输入输出框架

FPGA之家 来源:鱼鹰谈单片机 作者:鱼鹰谈单片机 2021-07-23 10:45 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

在一个嵌入式系统中,可能存在许多输入或输出的IO口,输入有霍尔传感器红外对管等,输出有LED电源控制开关等。

如果说硬件可以一次成型,那么随便一份代码都可以完成IO的配置工作,但研发阶段的产品,硬件各种修改是难免的,每一次 IO 的修改,对于底层开发人员来说,可能都是一次挑战。

因为一旦有某一个 IO 配置错误,或者原来的配置没有修改正确(比如一个 IO 在原来的硬件适配中是输入,之后的硬件需要修改成输出),那么你很难查出来这是什么问题,因为这个时候不仅硬件修改了,软件也修改了,你需要先定位到底是软件问题还是硬件问题,所以一个好用的 IO 的配置框架就显得很有必要了。

有道友会说,不如使用 CubeMx 软件进行开发吧。

1、这个软件适用于 ST 单片机,以前还能用,现在,除非你家里有矿,不然谁用的起STM32?基本上都国产化了(虽然有些单片机号称兼容,但到底还是有些差异的)。

2、公司原本的代码就是使用标准库,只是因为IO 的变化,你就需要把整个库换掉吗?时间上允许吗?你确定修改后不会出现大问题?

3、国产化的芯片可没有所谓的标准库和HAL库供你选择,每一家都有各自的库,如果你的产品临时换方案怎么办?

4、HAL 效率问题。

今天鱼鹰介绍一个简单实用的框架,可用于快速增加或修改IO配置,甚至修改底层库。

假设有3个 LED 作为输出、3 个霍尔传感器作为输入:

输入配置代码:

#define GPIOx_Def GPIO_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)

#define BSP_GPIO_PUPD_NONE GPIO_Mode_IN_FLOATING#define BSP_GPIO_PUPD_PULLUP GPIO_Mode_IPU#define BSP_GPIO_PUPD_PULLDOWN GPIO_Mode_IPD

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); } }

调试的时候,我们可以很方便的查看每个 IO 的状态是怎样的,而不用管 0 或 1 到底代表什么意思:

输出配置代码:

#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); } }

这个框架有啥好处呢?

1、自动完成 GPIO 的时钟初始化工作,也就是说你只需要修改引脚即可,不必关心时钟配置,但对于特殊引脚(比如PB3),还是得另外配置才行。

2、应用和底层具体 IO 分离,这样一旦修改了 IO,应用代码不需要进行任何修改。

3、增加或删减 IO 变得很简单,增加 IO时,首先加入对应枚举,然后就可以添加对应的 IO 了。删除 IO时,只要屏蔽对应枚举值和引脚即可。

4、参数检查功能, IO 删除时,因为屏蔽了对应的枚举,所以编译时可以帮你发现问题,而增加 IO 时,它可以帮你在运行时检查该 IO是否进行配置了,可以防止因为失误导致的问题。

a82570d2-e10c-11eb-9e57-12bb97331649.png

5、更改库时可以很方便,只需要修改对应的宏即可,目前可以顺利在 GD32 和 STM32 库进行快速更换。

6、对于输入 IO 而言,可以方便的修改有效和无效状态,防止硬件修改有效电平。对于输出 IO 而言,可以设定初始 IO 电平状态。

7、代码简单高效,尽可能的复用代码,增加一个 IO 只需要很少的空间。

8、缺点就是,只对同种配置的 IO 可以这样用。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式系统
    +关注

    关注

    41

    文章

    3846

    浏览量

    134245

原文标题:简单实用IO输入输出框架

文章出处:【微信号:zhuyandz,微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    探索MOS - 1110Y - 0101E:PCIe接口隔离数字输入输出模块的卓越之选

    探索MOS - 1110Y - 0101E:PCIe接口隔离数字输入输出模块的卓越之选 在电子工程师的日常设计工作中,选择合适的输入输出模块至关重要。今天,我们就来深入了解款具有诸多优秀特性的产品
    的头像 发表于 05-13 11:40 335次阅读

    ADP5585:多功能输入输出扩展器的技术剖析

    ADP5585:多功能输入输出扩展器的技术剖析 在电子设计领域,输入输出扩展器是提升系统功能和灵活性的关键组件。ADP5585作为款具备强大功能的10输入/
    的头像 发表于 05-11 16:10 137次阅读

    如何基于现场需求选配PLC本地/远程IO

    很多刚接触工控的朋友,常常会被“本地IO”和“远程IO”这两概念搞混。 它们到底有什么区别?在实际项目中又该如何选择?今天我们就来次性说清楚。 什么是
    的头像 发表于 03-30 17:15 642次阅读
    如何基于现场需求选配PLC本地/远程<b class='flag-5'>IO</b>?

    AD74115:单通道软件可配置输入输出设备的深度解析

    的解决方案。本文将对AD74115进行详细剖析,探讨其特性、工作原理、应用场景以及相关注意事项。 文件下载: AD74115.pdf 、AD74115的特性亮点 1. 多功能可配置性 AD74115具有单通道软件可配置输入输出功能,
    的头像 发表于 03-25 15:45 309次阅读

    西门子模拟量输入输出具体的应用

    西门子模拟量输入输出在工业自动化和控制系统中有着广泛的应用,以下是对其具体应用的些详细阐述。
    的头像 发表于 03-12 16:54 442次阅读
    西门子模拟量<b class='flag-5'>输入输出</b>具体的应用

    SGM8431-1单运放:高输出电流与轨到轨输入输出的卓越之选

    SGM8431-1单运放:高输出电流与轨到轨输入输出的卓越之选 在电子设计领域,运算放大器作为种基础且关键的器件,其性能的优劣直接影响着整个电路的表现。今天我们要深入探讨的SGM8431 - 1单
    的头像 发表于 03-11 16:15 265次阅读

    SGM8710 | 低功耗、高性能的轨到轨输入输出比较器

    SGM8710 | 低功耗、高性能的轨到轨输入输出比较器 在电子设计领域,为了满足不断增长的低功耗和高性能需求,工程师们直在寻找合适的器件。SGM8710作为款单通道轨到轨输入输出
    的头像 发表于 03-10 15:50 358次阅读

    精密轨到轨输入输出运算放大器OP484:设计与应用全解析

    精密轨到轨输入输出运算放大器 OP184/OP284/OP484:设计与应用全解析 在电子工程师的日常设计工作中,运算放大器是至关重要的组件,它的性能优劣直接影响到整个电路的表现。今天,我们就来
    的头像 发表于 01-29 17:00 688次阅读

    LT1218L:精密轨到轨输入输出运算放大器的卓越之选

    的 LT1218/LT1219 精密轨到轨输入输出运算放大器,它在众多方面展现出了出色的性能,能够满足多种应用场景的需求。 文件下载: LT1218L.pdf 、关键特性剖析 1. 轨到轨输入输出
    的头像 发表于 01-28 17:15 855次阅读

    LT1219:精密轨到轨输入输出运算放大器的卓越之选

    LT1218/LT1219:精密轨到轨输入输出运算放大器的卓越之选 在电子工程师的日常设计工作中,运算放大器的选择对于电路性能的优劣起着关键作用。今天,我们就来深入探讨下 Linear
    的头像 发表于 01-27 10:05 448次阅读

    IGBT的原理,输入输出和控制信号

    IGBT的原理,输入输出和控制信号
    的头像 发表于 12-06 06:38 3309次阅读

    文带你读懂“脉冲输入输出模块”

    “脉冲输入输出模块”,正是这位默默无闻的“信号翻译官”。 、什么是脉冲输入输出模块? 简单来说, 脉冲输入/
    的头像 发表于 11-24 17:10 1388次阅读
    <b class='flag-5'>一</b>文带你读懂“脉冲<b class='flag-5'>输入输出</b>模块”

    GPIO输入输出调用方法的理解与分享

    Nuclei Studio中为GPIO的调用定义了七函数。 与GPIO输入输出的直接有关的是: GPIO_TypeDef *gpio可以选择为GPIOA
    发表于 10-29 06:24

    如何用FPGA实现4K视频的输入输出与处理

    在游戏、影视和显示领域,4K 已经成为标配。而今天,我们就来聊聊——如何用 FPGA 实现 4K 视频的输入输出与处理。
    的头像 发表于 10-15 10:47 2590次阅读
    如何用FPGA实现4K视频的<b class='flag-5'>输入输出</b>与处理

    模块通吃DI/DO/AI/AO!无线IO的万能信号采集方案

    无线IO通讯模块是种特殊的输入输出(I/O)设备,它采用无线通讯技术实现I/O信号的传输。以下是对无线IO通讯模块的详细介绍: 、定义与
    的头像 发表于 07-22 10:15 829次阅读
    <b class='flag-5'>一</b><b class='flag-5'>个</b>模块通吃DI/DO/AI/AO!无线<b class='flag-5'>IO</b>的万能信号采集方案