在RT-thread 2.0.0正式版中引入了pin設備作為雜類設備,其設備驅動文件pin.c在rt-thread-2.0.1\components\drivers\misc中,主要用於操作芯片GPIO, 如點亮led,按鍵等。同時對於相應的芯片平台,需要自行編寫底層gpio驅動,如gpio.c。本文主要涉及的pin設備文件有:驅動框架文件(pin.c,pin.h),底層硬件驅動文件(gpio.c,gpio.h)。在應用用PIN設備時,需要在rtconfig.h中宏定義#define RT_USING_PIN。
一、PIN設備驅動框架
在pin.c中定義了一個靜態的pin設備對象static struct rt_device_pin _hw_pin,其中結構體類型struct rt_device_pin在pin.h中定義為:
/* pin device and operations for RT-Thread */ struct rt_device_pin { struct rt_device parent; const struct rt_pin_ops *ops; };
struct rt_device_pin_mode { rt_uint16_t pin; //pin index in pins[] of gpio.c rt_uint16_t mode; }; struct rt_device_pin_status { rt_uint16_t pin; //pin index in pins[] of gpio.c rt_uint16_t status; }; struct rt_pin_ops { void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode); void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value); int (*pin_read)(struct rt_device *device, rt_base_t pin); /* TODO: add GPIO interrupt */ };
在pin.c中主要實現了_pin_read,_pin_write,_pin_control三個函數,同時將這三個函數注冊為_hw_pin設備的統一接口函數:
int rt_device_pin_register(const char *name, const struct rt_pin_ops *ops, void *user_data) { _hw_pin.parent.type = RT_Device_Class_Miscellaneous; _hw_pin.parent.rx_indicate = RT_NULL; _hw_pin.parent.tx_complete = RT_NULL; _hw_pin.parent.init = RT_NULL; _hw_pin.parent.open = RT_NULL; _hw_pin.parent.close = RT_NULL; _hw_pin.parent.read = _pin_read; _hw_pin.parent.write = _pin_write; _hw_pin.parent.control = _pin_control; _hw_pin.ops = ops; _hw_pin.parent.user_data = user_data; /* register a character device */ rt_device_register(&_hw_pin.parent, name, RT_DEVICE_FLAG_RDWR); return 0; }
最后,在pin.c文件中將rt_pin_mode,rt_pin_write,rt_pin_read三個函數加入到finsh的函數列表中,用於調試。
二、底層硬件驅動
在gpio.c中主要實現struct rt_pin_ops中的三個接口函數:stm32_pin_mode,stm32_pin_write,stm32_pin_read:
const static struct rt_pin_ops _stm32_pin_ops = { stm32_pin_mode, stm32_pin_write, stm32_pin_read, };
同時注冊 _hw_pin設備,其設備名稱為“pin”,PIN設備硬件初始化:
int stm32_hw_pin_init(void) { rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL); return 0; } INIT_BOARD_EXPORT(stm32_hw_pin_init);//stm32_hw_pin_init will be called in rt_components_board_init()
三、PIN設備初始化
在gpio.c中修改使用的IO口數組:
/* STM32 GPIO driver */ struct pin_index { int index; uint32_t rcc; GPIO_TypeDef *gpio; uint32_t pin; }; /* LED ->PD12,PD13,PD14,PD15; USER Button->PA0 */ static const struct pin_index pins[] = { { 0, RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_12}, //green { 1, RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_13}, //orange { 2, RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_14}, //red { 3, RCC_AHB1Periph_GPIOD, GPIOD, GPIO_Pin_15}, //blue { 4, RCC_AHB1Periph_GPIOA, GPIOA, GPIO_Pin_0}, //user button };
此外在gpio_pin.c的外設初始化函數中,需要先調用rt_device_open函數(盡管該函數沒有在底層實現),保證pin設備對象類的設備引用計數值ref_count不為0,這樣才可正常使用rt_device_control,rt_device_write,rt_device_read函數操作GPIO口:
static struct rt_device_pin_mode led_mode[]= { {0,PIN_MODE_OUTPUT}, {1,PIN_MODE_OUTPUT}, {2,PIN_MODE_OUTPUT}, {3,PIN_MODE_OUTPUT}, }; static struct rt_device_pin_status led_status[]= { {0,PIN_HIGH}, /* 0:green on */ {1,PIN_HIGH}, /* 1:orange on */ {2,PIN_HIGH}, /* 2:red on */ {3,PIN_HIGH}, /* 3:blue on */ {0,PIN_LOW}, /* 4:green off */ {1,PIN_LOW}, /* 5:orange off */ {2,PIN_LOW}, /* 6:red off */ {3,PIN_LOW}, /* 7:blue off */ }; /* it can't be PIN_MODE_INPUT_PULLUP, or the key always will keep PIN_HIGH status */ static struct rt_device_pin_mode key_mode = {4,PIN_MODE_INPUT}; static struct rt_device_pin_status key_status = {4,PIN_LOW}; static struct rt_device_pin * pin_device;
static rt_err_t gpio_pin_init(const char * pin_device_name) { pin_device = (struct rt_device_pin *)rt_device_find(pin_device_name); if(pin_device == RT_NULL) { rt_kprintf("pin device for gpio %s not found!\r\n", pin_device_name); return -RT_ENOSYS; } /* oflag has no meaning for pin device , so set to RT_NULL */ if(rt_device_open(&pin_device->parent, RT_NULL) == RT_EOK) { /* init led */ for(int i=0; i<(sizeof(led_mode)/sizeof(led_mode[0])); i++) { rt_device_control(&pin_device->parent, RT_NULL, &led_mode[i]); rt_device_write(&pin_device->parent, RT_NULL, &led_status[i], sizeof(led_status[i]));//init all led PIN_HIGH } /* init key */ rt_device_control(&pin_device->parent, RT_NULL, &key_mode); } return 0; } int rt_gpio_pin_init(void) { gpio_pin_init("pin"); return 0; } INIT_APP_EXPORT(rt_gpio_pin_init);