为了先了解contiki NG的文件结构,先上一张图:
共有6大文件:
OS内核心的:---------------> gpio-hal.h; gpio-hal.c 这是OS作者提供的
cc2358驱动库的:---------> gpio.h; gpio.c 这是芯片厂家提供的
用户要实现的:-------------> gpio-hal-arch.h; gpio-hal-arch.c 这是你要编写的
因为CONTIKI NG和CONTIKI 3.0有着兼容的设计关系,写了一堆新版\旧版本代码信息共存,看着就有点点乱。还有就是h头文件里面,多数是完整功能的宏函数(linux特性),
用惯了类似STM32库的人员,肯定是1W个不习惯,用上一段时间后,会慢慢适应过来。
其实我们只要,熟读和理解一个 或二个OS/DEV驱动的实现原理,就可以依此类推,快速NG上手升级我们应用能力!!就目前开始,我还在阅读
OS与CC2538的GPIO操作方法,希望能领悟其中的巧门!!
时间:2020-09-20
看了几天,发现gpio-hal-arch.h和gpio-hal.h还是蛮复杂的,所以我还是先搞定标准库的 gpio.h吧(是NG自带的 不是TI官网下载的)
首先CONTIKI NG提供好了,下面这些宏功能,我们可以直接用它对寄存器操作,只要有官方的寄存器数据就行了,
/** * \addtogroup cc2538 * @{ * * \defgroup cc2538-reg cc2538 Register Manipulation * * Macros for hardware access, both direct and via the bit-band region. * @{ * * \file * Header file with register manipulation macro definitions */ #ifndef REG_H_ #define REG_H_ #define REG(x) (*((volatile unsigned long *)(x))) #define REG_H(x) (*((volatile unsigned short *)(x))) #define REG_B(x) (*((volatile unsigned char *)(x))) #define REG_BIT_W(x, b) \ REG(((unsigned long)(x) & 0xF0000000) | 0x02000000 | \ (((unsigned long)(x) & 0x000FFFFF) << 5) | ((b) << 2)) #define REG_BIT_H(x, b) \ REG_H(((unsigned long)(x) & 0xF0000000) | 0x02000000 | \ (((unsigned long)(x) & 0x000FFFFF) << 5) | ((b) << 2)) #define REG_BIT_B(x, b) \ REG_B(((unsigned long)(x) & 0xF0000000) | 0x02000000 | \ (((unsigned long)(x) & 0x000FFFFF) << 5) | ((b) << 2)) #endif /* REG_H_ */
下面是我的GPIO的测试INIT初始化代码,整整搞了4个小时,不容易啊,不过还是比较理想的。
像下面的GPIO_SET_OUTPUT(x.x)等,都是使用了REG(X)进行操作的。
1 //配置RC3为输出,使用原NG的cc2538-dev的gpio.h文件实现。 2 //先查看一下,GPIO_C的DIR方向寄存器的内容,RC3xxxx 0xxx,是1就为输出 3 UARTprintf("RC3-DIR:%d\r\n",REG((GPIO_C_BASE) + GPIO_DIR)&0xff); 4 5 //将RC3 xxxx 0xxx方向寄存器设为1 6 GPIO_SET_OUTPUT(GPIO_C_BASE, GPIO_PIN_MASK(3)); 7 8 //打印出来确认一下RC3方向为1 9 UARTprintf("RC3-DIR:%d\r\n",REG((GPIO_C_BASE) + GPIO_DIR)&0xff); 10 11 //RC3为SET,打印确认 12 GPIO_SET_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)); 13 UARTprintf("SET-RC3-DATA:%d\r\n",GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 14 15 //RC3为CLR,打印确认 16 GPIO_CLR_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)); 17 UARTprintf("CLR-RC3-DATA:%d\r\n",GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 18 19 //RC3为SET,打印所有的GPIO确认一下, 20 GPIO_SET_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)); 21 UARTprintf("SET-PORTC-DATA:%d\r\n",REG((GPIO_C_BASE) + GPIO_DATA + ((0xff) << 2))); 22 23 //RC3为CLR,打印所有的GPIO确认一下, 24 GPIO_CLR_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)); 25 UARTprintf("CLR-PROTC-DATA:%d\r\n",REG((GPIO_C_BASE) + GPIO_DATA + ((0xff) << 2))); 26 27 //先读取值,以此值取反,达到闪烁LED目的测试 28 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 29 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 30 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 31 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 32 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)));
最后建一个时间事件进行点LED-RC3灯,如下代码
1 /*-----------------------CC2538-GPIO的测试------------*/ 2 /*---------------------------------------------------------------------------*/ 3 PROCESS(cc2538_GPIO_DEV, "cc2538_GPIO_DEV"); 4 /*---------------------------------------------------------------------------*/ 5 PROCESS_THREAD(cc2538_GPIO_DEV, ev, data) 6 { 7 static struct etimer et04; 8 static uint16_t count02=0; 9 //intitRC3(); 10 11 PROCESS_BEGIN(); 12 etimer_set(&et04, CLOCK_SECOND*1); 13 14 while(1) { 15 PROCESS_WAIT_UNTIL(etimer_expired(&et04)); 16 17 etimer_reset(&et04); 18 UARTprintf("GPIO_DEV:%d\r\n",count02++); 19 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 20 PROCESS_YIELD(); 21 } 22 23 PROCESS_END(); 24 } 25 //***********************************************************// 26 /* 所有自启动模式的PROCESSES */ 27 AUTOSTART_PROCESSES( 28 &cc2538_GPIO_DEV 29 30 );
运行效果,打印出来的寄存器值,和数据手册对了一下,它们是正确的,这个PC3正在1秒反转一次进行闪烁,说明CC2538的dve的gpio.h功能测试通过了
从上面信息,我们可以看到,RC3能成功的进行操作,上面提到的REG宏功能,以及GPIO的初化的方法,其实它适用于所有MCU,只要你有对应的寄存器手册就可以了。
另外,GPIO的输入功能大同小意,可以直接跳过。想吃透这个OS/dev/的gpio-hal.h,还是要想办法把gpio-hal-arch.h搞定,总不能用着OS系统,按照裸机方法来写代码吧??
所以GPIO-HAL还要继续进行研究。。。。
时间:2020-09-27
之前,我们用CC2538的dve的gpio.h文件,把GPIO驱动起来了,今天我们利用gpio-hal-arch.h功能,实现与OS的链接,原作者的有点复杂,作了适当简化。
首先打开ng4.5-develop\examples\MYdemo\user\cc2538-dev的gpio-hal-arch.h文件,如下:
/*---------------------------------------------------------------------------*/ /** * \addtogroup cc2538 * @{ * * \defgroup cc2538-gpio-hal CC2538 GPIO HAL实现 * * @{ * * \file * CC2538 GPIO HAL功能的头文件 * * \note * 不直接包含此H标头 */ /*---------------------------------------------------------------------------*/ #ifndef GPIO_HAL_ARCH_H_ #define GPIO_HAL_ARCH_H_ /*---------------------------------------------------------------------------*/ #include "contiki.h" #include "gpio.h" #include <stdint.h> /*---------------------------------------------------------------------------*/ #define PIN_TO_PORT(pin) (pin >> 3) #define PIN_TO_NUM(pin) (pin % 8) #define PIN_TO_PORT_BASE(pin) GPIO_PORT_TO_BASE(PIN_TO_PORT(pin)) /*---------------------------------------------------------------------------*/ #define gpio_hal_arch_init() do { /* 没做什么 */ } while(0) /* 打开端口引脚的中断 */ #define gpio_hal_arch_interrupt_enable(port, p) do { \ GPIO_ENABLE_INTERRUPT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ NVIC_EnableIRQ(PIN_TO_PORT(p)); \ } while(0); /* 关掉端口引脚的中断 */ #define gpio_hal_arch_interrupt_disable(port, p) \ GPIO_DISABLE_INTERRUPT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)) /* 端口引脚配置成输入 */ #define gpio_hal_arch_pin_set_input(port, p) do { \ GPIO_SOFTWARE_CONTROL(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ GPIO_SET_INPUT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ } while(0); /* 端口引脚配置成输出 */ #define gpio_hal_arch_pin_set_output(port, p) do { \ GPIO_SOFTWARE_CONTROL(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ GPIO_SET_OUTPUT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ } while(0); /* 端口引脚配置成1 */ #define gpio_hal_arch_set_pin(port, p) \ GPIO_SET_PIN(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)) /* 端口引脚配置成0 */ #define gpio_hal_arch_clear_pin(port, p) \ GPIO_CLR_PIN(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)) /* 读取引脚的电平 */ #define gpio_hal_arch_read_pin(port, p) \ (GPIO_READ_PIN(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)) == 0 ? 0 : 1) /*---------------------------------------------------------------------------*/ #endif /* GPIO_HAL_ARCH_H_ */ /*---------------------------------------------------------------------------*/ /** @} */
这一次,我们关注的是:
1 /* 端口引脚配置成输出 */ 2 #define gpio_hal_arch_pin_set_output(port, p) do { \ 3 GPIO_SOFTWARE_CONTROL(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 4 GPIO_SET_OUTPUT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 5 } while(0);
GPIO_SOFTWARE_CONTROL(x,x)是模式控制,就是说这个IO脚是软件直接操作,还是用于外设,如IIC ADC等功能。
我们细仔看,发现(port, p)的port其实没什么用的,完全可以不理它,我们可以用0进行输入,最后研究出来是这样进行操作的
1 /* 使用gpio-hal-arch.h文件实现 */ 2 gpio_hal_arch_pin_set_output(0,GPIO_PORT_PIN_TO_GPIO_HAL_PIN(GPIO_C_NUM, LEDS_ARCH_L3_PIN)); //OK 3 4 5 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 6 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 7 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 8 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 9 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 10 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 11 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 12 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)));14 15 16 UARTprintf("HAL NO:0x%x\r\n",GPIO_PORT_PIN_TO_GPIO_HAL_PIN(GPIO_C_NUM, LEDS_ARCH_L3_PIN)); //换为GPIO HAL引脚号:0x13,这个3就是引脚号3
经过仿真发现,我们成功点亮了RC3,这里面关键的是GPIO_PORT_PIN_TO_GPIO_HAL_PIN(PORT, PIN),该定义位于MYdemo\user\cc2538-dev的gpio.h里面,属于CC2358-DEV部份。
我们将它进行UARTprintf进行打印,发现它的值为0x13,应该是3号PIN脚,理论上就是其它文件的实现的PORT号,PIN号。我们改一下:
1 /* 使用gpio-hal-arch.h文件实现 */ 2 //gpio_hal_arch_pin_set_output(0,GPIO_PORT_PIN_TO_GPIO_HAL_PIN(GPIO_C_NUM, LEDS_ARCH_L3_PIN)); //OK 3 gpio_hal_arch_pin_set_output(0,0x13); //OK 4 5 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3))); 6 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(3)));
我的天,看看仿真的结果,RC3功能完全正常!那么0x11肯定是PIN号1了,同理0x12肯定是PIN号2了,同理0x14肯定是PIN号4了,
带着这个疑问,我们测试一下PIN号2,就用0x12:
1 gpio_hal_arch_pin_set_output(0,0x12); //OK 2 3 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(2), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(2))); 4 GPIO_WRITE_PIN(GPIO_C_BASE, GPIO_PIN_MASK(2), ~GPIO_READ_PIN(GPIO_C_BASE, GPIO_PIN_MASK(2)));
我的天呀,看看仿真的结果,RC2被点亮了,哈哈哈!!!不过我们写成gpio_hal_arch_pin_set_output(0,GPIO_PORT_PIN_TO_GPIO_HAL_PIN(GPIO_C_NUM, LEDS_ARCH_L3_PIN)); 好一点,因为可读性高一点,比竟有时要让别人阅读你的代码。
如果你天天写成(0,0x12)的话,肯定要被领导P的。另外0也不能写的,应该写成GPIO_HAL_NULL_PORT被定义成0的,看来数字符号难于理解,英文字母才能更好的表示用意,当然注示还是用中文的^_^
总结:在多数情况下,开发者要以二进制或16进制的数学符号,去理解编译器的行为,实际上也就是OS原作者的思想!(本质是数学)
还有一个ng4.5-develop\os\dev的gpio-hal.h文件,研究完这个OS/dev的gpio-hal.h,基本上GPIO就差不多了,最后就是每个RA RC RB引脚去测试一下,加深理解,从而换其它芯片时能快速形成应用,为自己加油!!
时间:2020-10-12
在ng4.5-develop\os\dev的gpio-hal.h文件中,其实它分为俩大部分,gpio-hal.h第1部份如下:
1 /** 2 * \addtogroup dev 3 * @{ 4 * 5 * \defgroup: gpio-hal GPIO硬件抽象层 6 * 7 * 8 * @{ 9 * 10 * \file 11 * GPIO HAL的头文件 12 */ 13 14 15 /*---------------------------------------------------------------------------*/ 16 #ifndef GPIO_HAL_H_ 17 #define GPIO_HAL_H_ 18 19 20 /*---------------------------------------------------------------------------*/ 21 #include "contiki.h" 22 #include <stdint.h> 23 24 25 /*---------------------------------------------------------------------------*/ 26 /** 27 * \brief 指定HAL是否应支持端口/引脚约定 28 * 29 * Some MCUs specify GPIOs as a port/pin combination, whereas some others 30 * only use a pin number. Our GPIO HAL supports both conventions in a portable 31 * fashion and this define is used to set the HAL in the desired of the two 32 * modes. 33 * 34 * The port developer should define GPIO_HAL_CONF_PORT_PIN_NUMBERING as a if 35 * the platform uses port/pin numbering, or to 0 if the platform only uses 36 * a simple number. 37 */ 38 #ifdef GPIO_HAL_CONF_PORT_PIN_NUMBERING //cc2538-def.h有约 39 #define GPIO_HAL_PORT_PIN_NUMBERING GPIO_HAL_CONF_PORT_PIN_NUMBERING //选定 40 #else 41 #define GPIO_HAL_PORT_PIN_NUMBERING 1 42 #endif 43 44 45 /*---------------------------------------------------------------------------*/ 46 /** 47 * \brief 指定是否需要基于软件的引脚切换 48 * 49 * Some MCUs allow GPIO pin toggling via direct register access. For these 50 * MCUs, define GPIO_HAL_CONF_ARCH_SW_TOGGLE to 0 and then implement 51 * gpio_hal_arch_toggle_pin() and gpio_hal_arch_toggle_pins() 52 * 53 * \sa gpio_hal_arch_toggle_pin() 54 * \sa gpio_hal_arch_toggle_pins() 55 */ 56 #ifdef GPIO_HAL_CONF_ARCH_SW_TOGGLE //cc2538-def.h有约 57 #define GPIO_HAL_ARCH_SW_TOGGLE GPIO_HAL_CONF_ARCH_SW_TOGGLE 58 #else 59 #define GPIO_HAL_ARCH_SW_TOGGLE 1 60 #endif 61 62 63 /*---------------------------------------------------------------------------*/ 64 /** 65 * \brief 方便宏,用于此宏的端口参数 66 * 67 * Use this as the port \e argument of macros when GPIO_HAL_PORT_PIN_NUMBERING 68 * is zero 69 */ 70 #define GPIO_HAL_NULL_PORT 0 //暂时理解为,不使用PORT端口参数 71 /*---------------------------------------------------------------------------*/ 72 73 typedef uint8_t gpio_hal_pin_t; //GPIO引脚号表示 74 typedef uint8_t gpio_hal_port_t; //表示端口的数据结构 75 typedef uint32_t gpio_hal_pin_cfg_t; //GPIO引脚配置,是不是很多余?用uintXX_t不好? 76 77 /** 78 * \brief 指定设备上的引脚总数 79 * 80 * 如果GPIO_HAL_PORT_PIN_NUMBERING为非零,则此宏无效。 81 */ 82 #ifdef GPIO_HAL_CONF_PIN_COUNT 83 #define GPIO_HAL_PIN_COUNT GPIO_HAL_CONF_PIN_COUNT 84 #else 85 #define GPIO_HAL_PIN_COUNT 32 86 #endif 87 88 #if GPIO_HAL_PIN_COUNT > 32 && !GPIO_HAL_PORT_PIN_NUMBERING 89 typedef uint64_t gpio_hal_pin_mask_t; 90 #else 91 /** 92 * \brief GPIO 引脚掩码表示 93 * 94 * 可用于使用单个变量表示多个引脚的掩码 95 * 当GPIO_HAL_PORT_PIN_NUMBERING为非零时,此类变量只能用于表示同一端口内的引脚。 96 */ 97 typedef uint32_t gpio_hal_pin_mask_t; 98 #endif 99 100 101 /*---------------------------------------------------------------------------*/ 102 #if GPIO_HAL_PORT_PIN_NUMBERING 103 typedef void (*gpio_hal_callback_t)(gpio_hal_port_t port, 104 gpio_hal_pin_mask_t pin_mask); 105 #else 106 typedef void (*gpio_hal_callback_t)(gpio_hal_pin_mask_t pin_mask); 107 #endif 108 109 110 /*---------------------------------------------------------------------------*/ 111 #define GPIO_HAL_PIN_CFG_PULL_NONE 0x00 112 #define GPIO_HAL_PIN_CFG_PULL_UP 0x01 113 #define GPIO_HAL_PIN_CFG_PULL_DOWN 0x02 114 #define GPIO_HAL_PIN_CFG_PULL_MASK (GPIO_HAL_PIN_CFG_PULL_UP | \ 115 GPIO_HAL_PIN_CFG_PULL_DOWN) 116 117 #define GPIO_HAL_PIN_CFG_HYSTERESIS 0x10 118 119 #define GPIO_HAL_PIN_CFG_EDGE_NONE 0x00 120 #define GPIO_HAL_PIN_CFG_EDGE_RISING 0x04 121 #define GPIO_HAL_PIN_CFG_EDGE_FALLING 0x08 122 #define GPIO_HAL_PIN_CFG_EDGE_BOTH (GPIO_HAL_PIN_CFG_EDGE_RISING | \ 123 GPIO_HAL_PIN_CFG_EDGE_FALLING) 124 125 #define GPIO_HAL_PIN_CFG_INT_DISABLE 0x00 126 #define GPIO_HAL_PIN_CFG_INT_ENABLE 0x80 127 #define GPIO_HAL_PIN_CFG_INT_MASK 0x80 128 129 130 /*---------------------------------------------------------------------------*/ 131 /** 132 * \brief GPIO 事件处理程序的数据类型 133 * 134 * GPIO事件处理程序是在引脚触发事件时调用的函数。 可以通过设置相应引脚的位置, 135 * 但在\ e pin_mask中,注册同一处理程序以处理多个引脚的事件。 136 * 如果GPIO_HAL_PORT_PIN_NUMBERING不为零,则每个端口需要一个单独的处理程序。 137 */ 138 typedef struct gpio_hal_event_handler_s { 139 struct gpio_hal_event_handler_s *next; 140 gpio_hal_callback_t handler; 141 #if GPIO_HAL_PORT_PIN_NUMBERING 142 gpio_hal_port_t port; 143 #endif 144 gpio_hal_pin_mask_t pin_mask; 145 } gpio_hal_event_handler_t; 146 147 148 /*---------------------------------------------------------------------------*/ 149 #define GPIO_HAL_PIN_UNKNOWN 0xFF //未知 GPIO 150 151 152 /*---------------------------------------------------------------------------*/ 153 /** 154 * \name 核心 GPIO 功能 155 * 156 * HAL 本身实现的功能 157 * @{ 158 */ 159 160 void gpio_hal_init(void); //初始化 Gpio HAL 161 162 /** 163 * \brief 注册一个函数,每当引脚触发事件时,请调用该函数 164 * \param handler The handler representation 165 * 166 * The handler must be pre-allocated statically by the caller. 167 * 168 * This function can be used to register a function to be called by the HAL 169 * whenever a GPIO interrupt occurs. 170 * 171 * \sa gpio_hal_event_handler 172 */ 173 void gpio_hal_register_handler(gpio_hal_event_handler_t *handler); 174 175 #if GPIO_HAL_PORT_PIN_NUMBERING 176 /** 177 * \brief 独立于平台的 GPIO 事件处理程序 178 * \param port The GPIO port, if applicable 179 * \param pins OR mask of pins that generated an event 180 * 181 * Whenever a GPIO input interrupt occurs (edge or level detection) and an ISR 182 * is triggered, the ISR must call this function, passing as argument an ORd 183 * mask of the pins that triggered the interrupt. This function will then 184 * call the registered event handlers (if any) for the pins that triggered the 185 * event. The platform code should make no assumptions as to the order that 186 * the handlers will be called. 187 * 188 * If a pin set in the mask has an event handler registered, this function 189 * will call the registered handler. 190 * 191 * If GPIO_HAL_PORT_PIN_NUMBERING is non-zero the function will also accept 192 * as its first argument the port associated to the pins that triggered the 193 * edge detection. 194 * 195 * This function will not clear any CPU interrupt flags, this should be done 196 * by the calling ISR. 197 * 198 * \sa gpio_hal_register_handler 199 */ 200 void gpio_hal_event_handler(gpio_hal_port_t port, gpio_hal_pin_mask_t pins); 201 #else 202 void gpio_hal_event_handler(gpio_hal_pin_mask_t pins); 203 #endif 204 205 /** 206 * \brief 将引脚转换为引脚掩码 207 * \param pin The pin 208 * \return The corresponding mask 209 */ 210 #define gpio_hal_pin_to_mask(pin) ((gpio_hal_pin_mask_t)1 << (pin)) 211 /** @} */
gpio-hal.h第2部份,经过分析源码发现,每个函数的套路都是一模一样的,我们取一个进行研究,达到举1反3:
1 /*---------------------------------------------------------------------------*/ 2 /** 3 * \name 由平台代码提供的GPIO引脚操作功能。 4 * 5 * 所有功能都有两种方式: 6 * 第1种->当 GPIO_HAL_PORT_PIN_NUMBERING 非0时,期望将gpio_hal_port_t作为参数 7 * 第2种->当 GPIO_HAL_PORT_PIN_NUMBERING 为0时,不期望将gpio_hal_port_t作为参数 8 * 9 * 为了要达到代码的可移植性,所有与平台无关的代码都应使用这些宏来操纵GPIO,而不是 10 * 直接使用port_ / no_port_函数。 11 * 12 * 13 * 下面的所有功能必须由平台的开发人员提供。HAL为开发人员提供了许多有关如何提供所需功能的选项。 14 * 15 * -开发人员可以提供符号。 例如,开发人员可以创建一个.c文件并实现一个名为 16 * gpio_hal_arch_set_port_pin()的函数。 在这种情况下,开发人员只需为适用的 17 * gpio_hal_arch_port_foo / gpio_hal_arch_no_port_foo提供符号。 18 * 19 * -开发人员可以提供一个类似于函数的宏,该宏的名称与此处声明的操作宏之一相同。 在这种情况下, 20 * 预处理器将删除此处的声明。 例如,开发人员可以执行以下操作: 21 * \code 22 * #define gpio_hal_arch_write_pin(port, pin, v) sdk_function(port, pin, v) 23 * \endcode 24 * 25 * -开发人员可以提供静态内联实现。 为此工作,开发人员可以执行以下操作: 26 * \code 27 * #define gpio_hal_arch_set_pin(port, pin) set_pin(port, pin) 28 * static inline void set_pin(gpio_hal_port_t port, gpio_hal_pin_t pin) { ... } 29 * \endcode 30 * 31 * 在后两种情况下,开发人员可能会在头文件中提供实现。 在这种情况下,平台的配置文件之一 32 * 必须为该标头的名称定义GPIO_HAL_CONF_ARCH_HDR_PATH文件。 例如: 33 * \code 34 * #define GPIO_HAL_CONF_ARCH_HDR_PATH "dev/gpio-hal-arch.h" 35 * \endcode 36 * @{ 37 */ 38 39 /*---------------------------------------------------------------------------*/ 40 /* Include Arch-Specific conf */ 41 #ifdef GPIO_HAL_CONF_ARCH_HDR_PATH 42 #include "gpio-hal-arch.h" 43 #endif /* GPIO_HAL_CONF_ARCH_HDR_PATH */ 44 45 /*---------------------------------------------------------------------------*/ 46 #ifndef gpio_hal_arch_pin_set_output 47 /** 48 * \brief 将引脚配置为GPIO输出 49 * \param port The GPIO port 50 * \param pin The GPIO pin number (0...GPIO_HAL_PIN_COUNT - 1) 51 * 52 * The implementation of this function also has to make sure that \e pin is 53 * configured as software-controlled GPIO. 54 * 55 * It is the platform developer's responsibility to provide an implementation. 56 * 57 * The implementation can be provided as a global symbol, an inline function 58 * or a function-like macro, as described above. 59 * 60 * \note Code should not call this function directly. Use GPIO manipulation 61 * macros instead. 62 */ 63 void gpio_hal_arch_port_pin_set_output(gpio_hal_port_t port, 64 gpio_hal_pin_t pin); 65 66 /** 67 * \brief 将引脚配置为GPIO输出 68 * \param pin The GPIO pin number (0...GPIO_HAL_PIN_COUNT - 1) 69 * 70 * The implementation of this function also has to make sure that \e pin is 71 * configured as software-controlled GPIO. 72 * 73 * It is the platform developer's responsibility to provide an implementation. 74 * 75 * The implementation can be provided as a global symbol, an inline function 76 * or a function-like macro, as described above. 77 * 78 * \note Code should not call this function directly. Use GPIO manipulation 79 * macros instead. 80 */ 81 void gpio_hal_arch_no_port_pin_set_output(gpio_hal_pin_t pin); 82 83 #if GPIO_HAL_PORT_PIN_NUMBERING 84 #define gpio_hal_arch_pin_set_output(port, pin) \ 85 gpio_hal_arch_port_pin_set_output(port, pin) 86 #else 87 #define gpio_hal_arch_pin_set_output(port, pin) \ 88 gpio_hal_arch_no_port_pin_set_output(pin) 89 #endif /* GPIO_HAL_PORT_PIN_NUMBERING */ 90 #endif /* gpio_hal_arch_pin_set_output */
前面先定义好2个功能函数,这里是gpio_hal_arch_port_pin_set_output(xx,xx) 和 gpio_hal_arch_no_port_pin_set_output(xx)
然后在利用GPIO_HAL_PORT_PIN_NUMBERING条件,是否使用PORT参数,过程实现如下:
1 #if GPIO_HAL_PORT_PIN_NUMBERING 2 #define gpio_hal_arch_pin_set_output(port, pin) \ 3 gpio_hal_arch_port_pin_set_output(port, pin) 4 #else 5 #define gpio_hal_arch_pin_set_output(port, pin) \ 6 gpio_hal_arch_no_port_pin_set_output(pin) 7 #endif /* GPIO_HAL_PORT_PIN_NUMBERING */
上面这个gpio_hal_arch_pin_set_output功能是在cc2538-dev的gpio-hal-arch.h已经实现了的,我们在这之前也已经测试过了!!果然很有灵性,
1 * -开发人员可以提供一个类似于函数的宏,该宏的名称与此处声明的操作宏之一相同。 在这种情况下, 2 * 预处理器将删除此处的声明。 例如,开发人员可以执行以下操作: 3 * \code 4 * #define gpio_hal_arch_pin_set_output(port, pin) sdk_function(port, pin) 5 * \endcode
我们在回到cc2538-dev的gpio-hal-arch.h的内容:
1 /* 端口引脚配置成输出 */ 2 #define gpio_hal_arch_pin_set_output(port, p) do { \ 3 GPIO_SOFTWARE_CONTROL(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 4 GPIO_SET_OUTPUT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 5 } while(0);
这些逻辑关系的文件要来回看、来回阅读,才很很好的理解,为了一目了然 我们整理在一个文件里:
1 /*---------------------------------------------------------------------------*/ 2 //在\os\dev的gpio-hal.h的信息 3 #ifndef gpio_hal_arch_pin_set_output 4 void gpio_hal_arch_port_pin_set_output(gpio_hal_port_t port, 5 gpio_hal_pin_t pin); 6 7 8 void gpio_hal_arch_no_port_pin_set_output(gpio_hal_pin_t pin); 9 10 #if GPIO_HAL_PORT_PIN_NUMBERING 11 #define gpio_hal_arch_pin_set_output(port, pin) \ 12 gpio_hal_arch_port_pin_set_output(port, pin) 13 #else 14 #define gpio_hal_arch_pin_set_output(port, pin) \ 15 gpio_hal_arch_no_port_pin_set_output(pin) 16 #endif /* GPIO_HAL_PORT_PIN_NUMBERING */ 17 #endif /* gpio_hal_arch_pin_set_output */ 18 19 20 21 /*---------------------------------------------------------------------------*/ 22 //在cc2538-dev的gpio-hal-arch.h的信息 23 /* 端口引脚配置成输出 */ 24 #define gpio_hal_arch_pin_set_output(port, p) do { \ 25 GPIO_SOFTWARE_CONTROL(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 26 GPIO_SET_OUTPUT(PIN_TO_PORT_BASE(p), GPIO_PIN_MASK((p) % 8)); \ 27 } while(0); 28 29 30 31 /*---------------------------------------------------------------------------*/ 32 // 我们在进程中使用的信息 33 /* 端口引脚配置成输出 */ 34 /* 使用gpio-hal-arch.h文件已经实现dev的gpio-hal.h内容 */ 35 gpio_hal_arch_pin_set_output(0,GPIO_PORT_PIN_TO_GPIO_HAL_PIN(GPIO_C_NUM, LEDS_ARCH_L3_PIN)); //OK
这样看够清楚了吧?哈哈。说白了define时,只要gpio-hal-arch.h的名字和gpio-hal.h的名字一样,就直接被转移到了gpio-hal-arch.h功能,
所有的细节都在gpio-hal-arch.h文件内,你明白了吗??