int main(void) { sysclk_init(); board_init(); while (1){ gpio_toggle_pin(LED0_GPIO); delay_ms(500); } } 找到函数,gpio_toggle_pin(LED0_GPIO);的出处;可以找到如下代码 #define gpio_toggle_pin(io_id) ioport_toggle_pin(io_id) 然后找到ioport_toggle_pin(io_id)函数。如下 __always_inline static inline void ioport_toggle_pin(port_pin_t pin) { arch_ioport_toggle_pin_level(pin); } 再找arch_ioport_toggle_pin_level(pin);函数 _always_inline static void arch_ioport_toggle_pin_level(ioport_pin_t pin) { PORT_t *base = arch_ioport_pin_to_base(pin); base->PINCRL = arch_ioport_pin_to_mask(pin); } 可以看到对结构体成员的PINCRL赋值,可以找到结构体如下 typedef struct PORT_struct { volatile uint8_t PINCRL; /*I/O Port PIN DATA READ ONLY */ volatile uint8_t DIR; /*I/O Port Data Direction Set */ volatile uint8_t PORTDATA; /* I/OPort DATA register */ } PORT_t; 这个结构体定义了PORT读写的结构体的成员,并对成员PINCRL成员赋值 先看看PORT_t *base = arch_ioport_pin_to_base(pin);这个函数,这个函数是定义了一个指向PORT_t的指针,找到arch_ioport_pin_to_base(pin)函数 __always_inline static PORT_t *arch_ioport_pin_to_base(ioport_pin_t pin) { return arch_ioport_port_to_base(pin >> 3); } 再找到arch_ioport_port_to_base(pin >> 3)函数 __always_inline static PORT_t *arch_ioport_port_to_base(uint8_t port) { return (PORT_t *)((uintptr_t)(IOPORT_BASE_ADDRESS + (port * IOPORT_PORT_OFFSET))); } 很容易的看到,这个指针的地址IOPORT_BASE_ADDRESS +(port * IOPORT_PORT_OFFSET),是端口的基地址加上偏移地址,可以分别找到基地址IOPORT_BASE_ADDRESS 0x20和偏移地址IOPORT_PORT_OFFSET 0x03 然后看看base->PINCRL = arch_ioport_pin_to_mask(pin);这个函数对结构体PORT_t成员PINCRL赋值,看看arch_ioport_pin_to_mask(pin)这个函数 __always_inlinestatic ioport_port_mask_t arch_ioport_pin_to_mask (ioport_pin_t pin) { return 1U << (pin & 0x07); }这个就是选择用到的PORT口,然后通过base->PINCRL = arch_ioport_pin_to_mask(pin)赋给PINCRL 总体感觉这个底层库很复杂,原本一个函数就可以搞定的,还要分成3,4个,或者5,6个函数,不知道ATMEL公司的工程师是怎么想的,原本一个8位的单片机就应该很容易入门的,非要搞得这么复杂,要搞定它还是需要大量的时间。大量的宏定义,比如 typedef uint8_t ioport_mode_t; typedef uint8_t ioport_pin_t; typedef uint8_t ioport_port_t; typedef uint8_t ioport_port_mask_t; 非要定义这么多干嘛,刚开始就被这个搞晕了,看着就头痛,ATMEL工程师真的智商我是跟不上。本人感觉款单片机虽然是8位的,但不适合新手入门。
|