本文共 5019 字,大约阅读时间需要 16 分钟。
HAL层(Hardware abstraction layer 硬件抽象层) 的目的是为了屏蔽底层不同芯片平台的差异,从而使驱动层上面的软件不会随芯片平台而改变。AliOS Things定义了全面的HAL抽象层,这个系列主要介绍AliOS ThingsHAL层与不同芯片平台对接的poring要点,并举例说明。
Hal 对接系列1 —— Gpio driver porting
一. 接口定义说明
gpio 对外接口定义在 include/hal/soc下面,接口函数主要有以下几个:
int32_t hal_gpio_init(gpio_dev_t *gpio)int32_t hal_gpio_output_high(gpio_dev_t *gpio);int32_t hal_gpio_output_low(gpio_dev_t *gpio);int32_t hal_gpio_output_toggle(gpio_dev_t *gpio);int32_t hal_gpio_input_get(gpio_dev_t *gpio, uint32_t *value);int32_t hal_gpio_enable_irq(gpio_dev_t *gpio, gpio_irq_trigger_t trigger, gpio_irq_handler_t handler, void *arg);int32_t hal_gpio_disable_irq(gpio_dev_t *gpio);int32_t hal_gpio_clear_irq(gpio_dev_t *gpio);int32_t hal_gpio_finalize(gpio_dev_t *gpio)
其中,结构体gpio_dev_t 定义为:
typedef struct { uint8_t port; /* gpio port */ gpio_config_t config; /* gpio config */ void *priv; /* priv data */} gpio_dev_t;
接口函数的含义清晰明了,这里用户有两种方式可以调用驱动:
二. 接口使用说明
二者调用方式在hal层没有显著区别。以第一种为例:
调用gpio驱动时,首先定义gpio_dev_t gpio_1 = {KEY_1, OUTPUT, &value} 即使用gpio 的输出功能,可以使用hal_gpio_output_high(&gpio_1);hal_gpio_output_low(& gpio_1);hal_gpio_output_toggle(& gpio_1);
等接口函数操作gpio输出高、低电平或者反转电平。
gpio_dev_t gpio_2 = {KEY_2, INPUT, NULL}
即使用gpio的输入功能,可以使用 hal_gpio_input_get(&gpio_2 , uint32_t *value) 等接口函数操作读取gpio电平值;gpio_dev_t gpio_3 = {KEY_3, IRQ_MODE, &value}
即使用gpio的输入中断功能,value值为上升沿、下降沿和边沿触发三种模式,可以使用hal_gpio_enable_irq(&gpio_3, gpio_irq_trigger_t trigger, gpio_irq_handler_t handler, void *arg);
进行中断初始化,用户还需要自行提供中断处理函数handler,定义如下:
typedef void (gpio_irq_handler_t)(void arg);具体的操作示例,可以参考aos/example/peri_deiver_test_developerkit/peri_test.c
三. HAL层对接要点
HAL层需要严格按照位于include/hal/soc/gpio.h的实现进行对接, 新建两个文件hal_gpio_xxx.c和hal_gpio_xxx.h,将封装层代码放到这两个文件中,实现上面列出的接口函数。
hal_gpio_init是gpio初始化,不同系列芯片的初始化方式差异较大,我们往往无法直接调用芯片厂商的初始化函数,此时需要对接口进行一些转换以对接芯片厂商的驱动。举例说明。
ST系列:
我们最终需要的gpio初始化接口即:int32_t hal_gpio_init(gpio_dev_t *gpio) ST系列芯片驱动的gpio初始化接口为:void HAL_GPIO_Init(GPIO_TypeDef GPIOx, GPIO_InitTypeDef GPIO_Init)其中的结构体GPIO_TypeDef 、GPIO_InitTypeDef 为ST芯片专用的驱动结构体定义,对接ST系列的gpio驱动时,需要手动对上述结构体进行定义,以满足继续调用ST的驱动的要求:定义GPIO_TypeDef *GPIOx;GPIO_InitTypeDef GPIO_InitStruct;/伪代码举例/
get_gpio_group(gpio, &GPIOx) 来获取GPIOxgpio_para_transform(gpio, &GPIO_InitStruct); 来获取 GPIO_InitStruct(get_gpio_group 和 gpio_para_transform 等函数的实现要点:根据GPIO_TypeDef 和GPIO_InitTypeDef 结构体的定义将 gpio_dev_t 实现转换 ,具体可参考ST驱动源码)有了GPIOx 和 GPIO_InitStruct,进一步调用ST提供gpio初始化函数 HAL_GPIO_Init再以NRF系列芯片为例:
NRF系列芯片驱动的gpio初始化接口为:ret_code_t renrf_drv_gpiote_init(void)对接该系列hal层时,可以直接调用底层驱动,需要注意的是,NRF的gpio初始化分输入gpio的初始化和输出gpio的初始化,即:ret_code_t nrf_drv_gpiote_out_init(nrf_drv_gpiote_pin_t pin, nrf_drv_gpiote_out_config_t const * p_config);ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin, nrf_drv_gpiote_in_config_t const * p_config, nrf_drv_gpiote_evt_handler_t evt_handler);
这里的 nrf_drv_gpiote_pin_t 、nrf_drv_gpiote_out_config_t 、nrf_drv_gpiote_in_config_t 等结构体定义也是NRF系列专用,对接该系列的gpio驱动时,需要手动对上述结构体进行定义,如:
static nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(false);static nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);int32_t hal_gpio_init(gpio_dev_t *gpio){ int32_t ret = -1; if (!nrf_drv_gpiote_is_init()) { ret = nrf_drv_gpiote_init(); if (ret != NRF_SUCCESS) return NRF_ERROR_INTERNAL; } switch (gpio->config) { case OUTPUT_PUSH_PULL: case OUTPUT_OPEN_DRAIN_NO_PULL: case OUTPUT_OPEN_DRAIN_PULL_UP: ret = nrf_drv_gpiote_out_init(gpio->port, &out_config); break; case IRQ_MODE: in_config.pull = NRF_GPIO_PIN_PULLUP; ret = nrf_drv_gpiote_in_init(gpio->port, &in_config, gpio->priv); default: ret = -1; break; } return ret;}
hal_gpio_output_high、
hal_gpio_output_low、hal_gpio_output_toggle 、hal_gpio_input_get等函数的HAL层对接比较简单,一般在gpio初始化后直接调用相应芯片的驱动即可,如ST系列: HAL_GPIO_WritePin、HAL_GPIO_ReadPin 进行gpio输入输出操作;NRF系列:nrf_drv_gpiote_out_set、nrf_drv_gpiote_out_clear、nrf_drv_gpiote_in_is_set进行gpio输入输出操作。对于gpio的输入中断的对接,ST系列使用了
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)进行gpio输入中断的响应,并在int32_t hal_gpio_enable_irq(gpio_dev_t *gpio, gpio_irq_trigger_t trigger, gpio_irq_handler_t handler, void *arg)
中定义中断响应的handler 和传入的参数arg,如:
ret = hal_gpio_enable_irq(&gpio1, IRQ_TRIGGER_RISING_EDGE, key_handle, NULL); if (ret != 0) { printf("hal_gpio_enable_irq key return failed.\n"); } void key_handle(void *arg) { /*key interupt相关操作*/ }
与ST不同,NRF系列在使用
ret_code_t nrf_drv_gpiote_in_init(nrf_drv_gpiote_pin_t pin, nrf_drv_gpiote_in_config_t const * p_config, nrf_drv_gpiote_evt_handler_t evt_handler);
最后一个参数evt_handler来表示中断响应函数,nrf 底层驱动会对这个参数进行判断,如果传入NULL,则表示不使用gpio输入中断功能。
综上所述, hal层porting需要根据不同芯片厂商的驱动的接口来进行。
转载地址:http://smyca.baihongyu.com/