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

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

3天内不再提示

简单实用的框架,可用于快速增加或修改IO配置

FPGA之家 来源:FPGA之家 作者:FPGA之家 2022-05-17 09:54 次阅读

在一个嵌入式系统中,可能存在许多输入或输出的IO口,输入有霍尔传感器红外对管等,输出有LED电源控制开关等。如果说硬件可以一次成型,那么随便一份代码都可以完成IO的配置工作,但研发阶段的产品,硬件各种修改是难免的,每一次 IO 的修改,对于底层开发人员来说,可能都是一次挑战。因为一旦有某一个 IO 配置错误,或者原来的配置没有修改正确(比如一个 IO 在原来的硬件适配中是输入,之后的硬件需要修改成输出),那么你很难查出来这是什么问题,因为这个时候不仅硬件修改了,软件也修改了,你需要先定位到底是软件问题还是硬件问题,所以一个好用的 IO 的配置框架就显得很有必要了。

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

1、这个软件适用于 ST 单片机,以前还能用,现在,除非你家里有矿,不然谁用的起STM32?基本上都国产化了(虽然有些单片机号称兼容,但到底还是有些差异的)。2、公司原本的代码就是使用标准库,只是因为IO 的变化,你就需要把整个库换掉吗?时间上允许吗?你确定修改后不会出现大问题?3、国产化的芯片可没有所谓的标准库和HAL库供你选择,每一家都有各自的库,如果你的产品临时换方案怎么办?4、HAL 效率问题。今天鱼鹰介绍一个简单实用的框架,可用于快速增加或修改IO配置,甚至修改底层库。假设有3个 LED 作为输出、3 个霍尔传感器作为输入:输入配置代码:
#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)

#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 到底代表什么意思:7f08e036-d57a-11ec-bce3-dac502259ad0.png输出配置代码:
#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是否进行配置了,可以防止因为失误导致的问题。7f58713c-d57a-11ec-bce3-dac502259ad0.png5、更改库时可以很方便,只需要修改对应的宏即可,目前可以顺利在 GD32 STM32 库进行快速更换。6、对于输入 IO 而言,可以方便的修改有效和无效状态,防止硬件修改有效电平。对于输出 IO 而言,可以设定初始 IO 电平状态。7、代码简单高效,尽可能的复用代码,增加一个 IO 只需要很少的空间。8、缺点就是,只对同种配置的 IO 可以这样用。

审核编辑 :李倩


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

    关注

    6032

    文章

    44513

    浏览量

    632879
  • 霍尔传感器
    +关注

    关注

    27

    文章

    705

    浏览量

    63069

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

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

收藏 人收藏

    评论

    相关推荐

    华纳云监视Linux磁盘IO性能命令:iotop,iostat,vmstat,atop,dstat,ioping

    。 top命令可用于查看CPU是否正在等待磁盘操作完成。“wa”度量标准显示IO等待,CPU等待IO完成所花费的时间(以百分比
    的头像 发表于 10-24 14:43 113次阅读

    io口和串口的区别 单片机有多少个io

    等。而串口,即串行通信接口(Serial Communication Interface),是一种数据通信方式,通过一条数据线按照顺序传送数据。IO口和串口在功能和用途上存在显著区别: 通信方式 : IO口 :实现简单的输入输出
    的头像 发表于 10-06 10:06 927次阅读

    【教程】比Modbus控制更简单!S7 200 SMART利用Profinet网关扩展IO

    PLC自带IO方法控制扩展IO。一配置Profinet网关首先导入Profinet网关的GSD文件,用于STEP7-MicroWINSMART组态软件
    的头像 发表于 09-20 08:07 449次阅读
    【教程】比Modbus控制更<b class='flag-5'>简单</b>!S7 200 SMART利用Profinet网关扩展<b class='flag-5'>IO</b>

    单片机io口怎么配置成输出口

    单片机IO配置成输出口的过程通常涉及对单片机内部寄存器的操作。不同型号的单片机在寄存器配置和编程方式上可能有所不同,但基本原理是相似的。以下是一个通用的步骤,用于将单片机
    的头像 发表于 09-14 14:33 624次阅读

    国产远程IO的在汽车生产装配的应用

    MR30系列远程IO使用简单、设计紧凑、性能卓越,适用于各种A汽车装配场景。
    的头像 发表于 07-12 15:28 302次阅读
    国产远程<b class='flag-5'>IO</b>的在汽车生产装配的应用

    是否可以修改ESP8266的wifi信标框架

    我想知道我是否可以修改ESP8266的 wifi 信标框架.
    发表于 07-12 12:15

    bootstrap框架和vue框架的区别

    Bootstrap和Vue都是目前非常流行的前端开发框架,它们各自具有独特的优势和特点。 设计理念 Bootstrap是一个基于HTML、CSS和JavaScript的前端开发框架,主要用于
    的头像 发表于 07-11 09:55 770次阅读

    远程IO与分布式IO的区别

    在工业自动化和控制系统设计中,远程IO(Input/Output)和分布式IO是两个重要的概念。它们各自具有独特的特点和优势,适用于不同的应用场景。本文将详细探讨远程IO与分布式
    的头像 发表于 06-15 15:57 2306次阅读

    暖通控制系统可用钡铼分布式IO系统BL207

    可编程逻辑控制功能,可替代DDC部分功能。 BACnet/IP IO系统BL207提供双网口支持交换机级联功能,支持标准的BACnet/IP协议,也可用于接入BACnet/IP控制器,可适配江森、霍
    的头像 发表于 05-24 15:35 341次阅读
    暖通控制系统<b class='flag-5'>可用</b>钡铼分布式<b class='flag-5'>IO</b>系统BL207

    自制测试框架(设置界面密码1)

    (设置界面密码为1) 目前支持的指令如下: 1.没有任何编程基础的人快速实现简易自动化制作; 2.测试后可以直接把数据和结果直接写入到MES; 3.可持续增加新的功能及模块; 4.快速实现点
    发表于 03-02 19:33

    EtherCAT IO的接线方法和流程是怎样的?

    EtherCAT IO的接线方法和流程是怎样的? EtherCAT是一种用于实时以太网通信的开放式通信协议,具有低延迟和高带宽的优势。 EtherCAT IO是EtherCAT网络中连接到IO
    的头像 发表于 02-02 16:57 1849次阅读

    更改晶振后如何修改配置

    GD32官方提供的固件库中使用的晶振配置一般为8M25M,如果读者使用其他频率的晶振如何修改配置呢?本文为大家讲解如何修改
    的头像 发表于 01-09 10:10 1089次阅读
    更改晶振后如何<b class='flag-5'>修改</b><b class='flag-5'>配置</b>?

    激光焊接行业的智能化选择钡铼分布式IO

    和稳定性。 钡铼技术分布式IO系统具有良好的扩展性和灵活性。随着激光焊接技术的发展,对于设备控制的需求也在不断变化。钡铼技术的分布式IO系统可以通过简单增加
    的头像 发表于 01-05 10:59 396次阅读
    激光焊接行业的智能化选择钡铼分布式<b class='flag-5'>IO</b>

    TQT507开发板如何修改和保存内核配置

    本文档介绍如何在开发时修改和保存内核配置,适用于开发板TQT507。 1.修改内核配置 编译时系统会先检测当前内核源码目录下是否存在.con
    的头像 发表于 12-28 14:13 421次阅读
    TQT507开发板如何<b class='flag-5'>修改</b>和保存内核<b class='flag-5'>配置</b>

    T507开发板如何修改和保存内核配置

    本文档介绍如何在开发时修改和保存内核配置,适用于开发板TQT507。1.修改内核配置编译时系统会先检测当前内核源码目录下是否存在.confi
    的头像 发表于 12-08 11:26 569次阅读
    T507开发板如何<b class='flag-5'>修改</b>和保存内核<b class='flag-5'>配置</b>