在前面的分析过程中,我们总结出了通用的pin驱动模板,下一步就是对瑞萨适配的pin驱动进行进一步的分析。
瑞萨对应实现分析
注册入口
const static struct rt_pin_ops _ra_pin_ops = { .pin_mode = ra_pin_mode, .pin_write = ra_pin_write, .pin_read = ra_pin_read, .pin_attach_irq = ra_pin_attach_irq, .pin_detach_irq = ra_pin_dettach_irq, .pin_irq_enable = ra_pin_irq_enable, .pin_get = ra_pin_get, }; #ifdef R_ICU_H static struct rt_pin_irq_hdr pin_irq_hdr_tab[RA_IRQ_MAX] = {0}; struct ra_pin_irq_map pin_irq_map[RA_IRQ_MAX] = {0}; static void ra_irq_tab_init(void) { for (int i = 0; i < RA_IRQ_MAX; ++i) { pin_irq_hdr_tab[i].pin = -1; pin_irq_hdr_tab[i].mode = 0; pin_irq_hdr_tab[i].args = RT_NULL; pin_irq_hdr_tab[i].hdr = RT_NULL; } } static void ra_pin_map_init(void) { #ifdef VECTOR_NUMBER_ICU_IRQ0 pin_irq_map[0].irq_ctrl = &g_external_irq0_ctrl; pin_irq_map[0].irq_cfg = &g_external_irq0_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ1 pin_irq_map[1].irq_ctrl = &g_external_irq1_ctrl; pin_irq_map[1].irq_cfg = &g_external_irq1_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ2 pin_irq_map[2].irq_ctrl = &g_external_irq2_ctrl; pin_irq_map[2].irq_cfg = &g_external_irq2_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ3 pin_irq_map[3].irq_ctrl = &g_external_irq3_ctrl; pin_irq_map[3].irq_cfg = &g_external_irq3_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ4 pin_irq_map[4].irq_ctrl = &g_external_irq4_ctrl; pin_irq_map[4].irq_cfg = &g_external_irq4_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ5 pin_irq_map[5].irq_ctrl = &g_external_irq5_ctrl; pin_irq_map[5].irq_cfg = &g_external_irq5_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ6 pin_irq_map[6].irq_ctrl = &g_external_irq6_ctrl; pin_irq_map[6].irq_cfg = &g_external_irq6_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ7 pin_irq_map[7].irq_ctrl = &g_external_irq7_ctrl; pin_irq_map[7].irq_cfg = &g_external_irq7_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ8 pin_irq_map[8].irq_ctrl = &g_external_irq8_ctrl; pin_irq_map[8].irq_cfg = &g_external_irq8_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ9 pin_irq_map[9].irq_ctrl = &g_external_irq9_ctrl; pin_irq_map[9].irq_cfg = &g_external_irq9_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ10 pin_irq_map[10].irq_ctrl = &g_external_irq10_ctrl; pin_irq_map[10].irq_cfg = &g_external_irq10_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ11 pin_irq_map[11].irq_ctrl = &g_external_irq11_ctrl; pin_irq_map[11].irq_cfg = &g_external_irq11_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ12 pin_irq_map[12].irq_ctrl = &g_external_irq12_ctrl; pin_irq_map[12].irq_cfg = &g_external_irq12_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ13 pin_irq_map[13].irq_ctrl = &g_external_irq13_ctrl; pin_irq_map[13].irq_cfg = &g_external_irq13_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ14 pin_irq_map[14].irq_ctrl = &g_external_irq14_ctrl; pin_irq_map[14].irq_cfg = &g_external_irq14_cfg; #endif #ifdef VECTOR_NUMBER_ICU_IRQ15 pin_irq_map[15].irq_ctrl = &g_external_irq15_ctrl; pin_irq_map[15].irq_cfg = &g_external_irq15_cfg; #endif } #endif /* R_ICU_H */ int rt_hw_pin_init(void) { // 硬件资源初始化,从后续的代码分析,可以发现宏 R_ICU_H 对应的功能应该是和pin中断有关, // 也就是说,在瑞萨的框架里,如果不使用中断,注册时甚至不需要初始化硬件资源 #ifdef R_ICU_H ra_irq_tab_init(); ra_pin_map_init(); #endif return rt_device_pin_register("pin", &_ra_pin_ops, RT_NULL); } rt_weak void rt_hw_board_init() { // 其他初始化 ...... /* pin驱动注册 */ #ifdef RT_USING_PIN rt_hw_pin_init(); #endif // 其他初始化 ...... }
设置pin模式入口
static void ra_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode) { fsp_err_t err; /* Initialize the IOPORT module and configure the pins */ // 完整看驱动代码会发现,这两个参数都是RASC生成的,应该是资源的预设置部分 err = R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg); if (err != FSP_SUCCESS) { LOG_E("GPIO open failed"); return; } switch (mode) { case PIN_MODE_OUTPUT: // 设置成输出模式 err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, BSP_IO_DIRECTION_OUTPUT); if (err != FSP_SUCCESS) { LOG_E("PIN_MODE_OUTPUT configuration failed"); return; } break; case PIN_MODE_INPUT: // 设置成输入模式 err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, BSP_IO_DIRECTION_INPUT); if (err != FSP_SUCCESS) { LOG_E("PIN_MODE_INPUT configuration failed"); return; } break; case PIN_MODE_OUTPUT_OD: // 设置成开漏模式 err = R_IOPORT_PinCfg(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, IOPORT_CFG_NMOS_ENABLE); if (err != FSP_SUCCESS) { LOG_E("PIN_MODE_OUTPUT_OD configuration failed"); return; } break; } }
从驱动上看,瑞萨适配的驱动并不支持输入上拉和输入下拉两种模式。也就是说,可能瑞萨整个RA系列平台硬件上都不支持这样的功能,也可能是这种接口未暴露出来。
写pin入口
typedef enum e_bsp_io_level { BSP_IO_LEVEL_LOW = 0, ///< Low BSP_IO_LEVEL_HIGH ///< High } bsp_io_level_t; static void ra_pin_write(rt_device_t dev, rt_base_t pin, rt_uint8_t value) { bsp_io_level_t level = BSP_IO_LEVEL_HIGH; if (value != level) { level = BSP_IO_LEVEL_LOW; } R_BSP_PinAccessEnable(); #ifdef SOC_SERIES_R9A07G0 R_IOPORT_PinWrite(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, (bsp_io_level_t)level); #else R_BSP_PinWrite(pin, level); #endif R_BSP_PinAccessDisable(); }
很好理解,就是一个写高低电平的操作。不过有个疑惑的点,前面输入的value非1就设为0,这是什么逻辑?正常操作不应该是非0就设为1吗?
读pin入口
static rt_ssize_t ra_pin_read(rt_device_t dev, rt_base_t pin) { if ((pin > RA_MAX_PIN_VALUE) || (pin < RA_MIN_PIN_VALUE)) { return -RT_EINVAL; } #ifdef SOC_SERIES_R9A07G0 bsp_io_level_t io_level; R_IOPORT_PinRead(&g_ioport_ctrl, (bsp_io_port_pin_t)pin, &io_level); return io_level; #else return R_BSP_PinRead(pin); #endif }
读接口就很好理解了,就是读电平并返回电平值。
注册中断回调入口
#ifdef R_ICU_H static rt_base_t ra_pin_get_irqx(rt_uint32_t pin) { PIN2IRQX_TABLE(pin) //其实就是通过pin编号转化成中断表编号 } #endif static rt_err_t ra_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args) { #ifdef R_ICU_H rt_int32_t irqx = ra_pin_get_irqx(pin); if (0 <= irqx && irqx < (sizeof(pin_irq_map) / sizeof(pin_irq_map[0]))) { int level = rt_hw_interrupt_disable(); if (pin_irq_hdr_tab[irqx].pin == irqx && pin_irq_hdr_tab[irqx].hdr == hdr && pin_irq_hdr_tab[irqx].mode == mode && pin_irq_hdr_tab[irqx].args == args) { // 如果都一样,说明已经注册了,就直接返回 rt_hw_interrupt_enable(level); return RT_EOK; } if (pin_irq_hdr_tab[irqx].pin != -1) { // 如果已注册其他回调函数,则不注册新的回调函数 rt_hw_interrupt_enable(level); return -RT_EBUSY; } // 注册回调函数 pin_irq_hdr_tab[irqx].pin = irqx; pin_irq_hdr_tab[irqx].hdr = hdr; pin_irq_hdr_tab[irqx].mode = mode; pin_irq_hdr_tab[irqx].args = args; rt_hw_interrupt_enable(level); } else return -RT_ERROR; return RT_EOK; #else return -RT_ERROR; #endif }
注销中断回调入口
static rt_err_t ra_pin_dettach_irq(struct rt_device *device, rt_base_t pin) { #ifdef R_ICU_H rt_int32_t irqx = ra_pin_get_irqx(pin); if (0 <= irqx && irqx < sizeof(pin_irq_map) / sizeof(pin_irq_map[0])) { int level = rt_hw_interrupt_disable(); if (pin_irq_hdr_tab[irqx].pin == -1) { // 如果已经注销,则直接返回 rt_hw_interrupt_enable(level); return RT_EOK; } // 注销资源 pin_irq_hdr_tab[irqx].pin = -1; pin_irq_hdr_tab[irqx].hdr = RT_NULL; pin_irq_hdr_tab[irqx].mode = 0; pin_irq_hdr_tab[irqx].args = RT_NULL; rt_hw_interrupt_enable(level); } else { return -RT_ERROR; } return RT_EOK; #else return -RT_ERROR; #endif }
使能关闭中断入口
#ifdef R_ICU_H void irq_callback(external_irq_callback_args_t *p_args) { rt_interrupt_enter(); if (p_args->channel == pin_irq_hdr_tab[p_args->channel].pin) { pin_irq_hdr_tab[p_args->channel].hdr(pin_irq_hdr_tab[p_args->channel].args); } rt_interrupt_leave(); }; #endif /* R_ICU_H */ static rt_err_t ra_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled) { #ifdef R_ICU_H rt_err_t err; rt_int32_t irqx = ra_pin_get_irqx(pin); if (PIN_IRQ_ENABLE == enabled) { if (0 <= irqx && irqx < sizeof(pin_irq_map) / sizeof(pin_irq_map[0])) { err = R_ICU_ExternalIrqOpen((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl, (external_irq_cfg_t const * const)pin_irq_map[irqx].irq_cfg); /* Handle error */ if (FSP_SUCCESS != err) { /* ICU Open failure message */ LOG_E("\r\n**R_ICU_ExternalIrqOpen API FAILED**\r\n"); return -RT_ERROR; } err = R_ICU_ExternalIrqEnable((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl); /* Handle error */ if (FSP_SUCCESS != err) { /* ICU Enable failure message */ LOG_E("\r\n**R_ICU_ExternalIrqEnable API FAILED**\r\n"); return -RT_ERROR; } } } else if (PIN_IRQ_DISABLE == enabled) { err = R_ICU_ExternalIrqDisable((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl); if (FSP_SUCCESS != err) { /* ICU Disable failure message */ LOG_E("\r\n**R_ICU_ExternalIrqDisable API FAILED**\r\n"); return -RT_ERROR; } err = R_ICU_ExternalIrqClose((external_irq_ctrl_t *const)pin_irq_map[irqx].irq_ctrl); if (FSP_SUCCESS != err) { /* ICU Close failure message */ LOG_E("\r\n**R_ICU_ExternalIrqClose API FAILED**\r\n"); return -RT_ERROR; } } return RT_EOK; #else return -RT_ERROR; #endif }
会发现,使能关闭接口,也就是直接调用瑞萨自己的应用适配层接口。另外,个人理解irq_callback这个函数应该也是RASC生成配置时填入的。
从字符串获取pin编号入口
static rt_base_t ra_pin_get(const char *name) { int pin_number = -1, port = -1, pin = -1; if (rt_strlen(name) != 4) return -1; if ((name[0] == 'P' || name[0] == 'p')) { if ('0' <= name[1] && name[1] <= '9') { port = (name[1] - '0') * 16 * 16; if ('0' <= name[2] && name[2] <= '9' && '0' <= name[3] && name[3] <= '9') { pin = (name[2] - '0') * 10 + (name[3] - '0'); pin_number = port + pin; return pin_number; } } else if ('A' <= name[1] && name[1] <= 'Z') { port = (name[1] - '0' - 7) * 16 * 16; if ('0' <= name[2] && name[2] <= '9' && '0' <= name[3] && name[3] <= '9') { pin = (name[2] - '0') * 10 + (name[3] - '0'); pin_number = port + pin; return pin_number; } } } return -1; }
由于瑞萨gpio命名规则都是P加三位数字,比如P101,因此此函数实际的功能也就将管脚的名称转换成驱动所需要的pin编号并返回给上层使用。
总结
至此,瑞萨适配的pin驱动已经分析完毕。从分析内容上看,我们大致可以获取以下信息:
1. 可能受限于硬件,瑞萨支持的pin驱动能并不完整
2. RASC生成时可以生成带中断和不带中断的配置,带中断时,应该会自动生成宏R_ICU_H,另外,可能有个irq_callback函数名需要填写。
3. 瑞萨的pin驱动,可以直接给到软件i2c框架使用,前提是,所选的管脚支持开漏模式。