设备表
简介
WM IoT SDK 使用设备表对设备进行配置管理,驱动代码和设备配置分离,使用更为方便。
设备表当前使用 C 语言文件来配置。
WM IoT SDK 当前使用的 C 语言配置文件默认位置为 components/wm_dt/config/wm_dt_hw.c
和 components/wm_dt/config/wm_dt_hw.h
。
当用户需要修改配置文件时,不推荐直接修改组件目录中的默认文件,
而是推荐从组件目录中复制一份至工程目录(例如复制至 main/dt
目录下),
然后修改工程目录下 main
组件中的 CMakeLists.txt
文件,添加
set(ADD_DT_C_FILES "dt/wm_dt_hw.c")
set(ADD_DT_H_FILES "dt")
甚至,希望使用多份配置文件时,可为:
set(ADD_DT_C_FILES "dt/dt_config1.c"
"dt/dt_config2.c"
)
set(ADD_DT_H_FILES "dt"
)
请注意,在工程目录中添加配置文件时,需保持 wm_dt_hw.h
文件名称不能变。
.c
文件可以任意改名。
在多份配置文件时,需要修改文件中的配置内部名称,让每份配置的内部名称不同。
如 dt_config1.c 配置内部名称为 my_config1:
WM_DT_TABLE_DEFINE(my_config1, (sizeof(dt_hw_table_entry) / sizeof(dt_hw_table_entry[0])), (void *)&dt_hw_table_entry[0]);
如 dt_config2.c 配置内部名称为 my_config2:
WM_DT_TABLE_DEFINE(my_config2, (sizeof(dt_hw_table_entry) / sizeof(dt_hw_table_entry[0])), (void *)&dt_hw_table_entry[0]);
C 语言配置文件格式
WM IoT SDK 使用的 C 语言配置文件,其中内容由如下格式组成(摘选了部分):
typedef struct {
uint8_t init_level;
uint8_t init_priority;
} wm_dt_hw_init_cfg_t;
typedef struct {
uint8_t irq_num; /**< @ref wm_irq_no_t */
uint8_t irq_priority;
} wm_dt_hw_irq_cfg_t;
typedef struct {
uint8_t pin_num; /**< @ref wm_gpio_num_t */
uint8_t pin_mux; /**< @ref wm_gpio_pin_mux_t */
uint8_t pin_dir; /**< @ref wm_gpio_dir_t */
uint8_t pin_pupd; /**< @ref wm_gpio_pupd_t */
} wm_dt_hw_pin_cfg_t;
typedef struct {
int baudrate; /**< @ref wm_uart_baudrate_t */
uint8_t parity; /**< @ref wm_uart_parity_t */
uint8_t stop_bits; /**< @ref wm_uart_stop_bits_t */
uint8_t data_bits; /**< @ref wm_uart_data_bits_t */
uint8_t flow_ctrl; /**< @ref wm_uart_flowctrl_t */
} wm_dt_hw_uart_cfg_t;
typedef struct {
wm_dt_hw_init_cfg_t init_cfg;
uint32_t reg_base;
wm_dt_hw_irq_cfg_t irq_cfg;
wm_dt_hw_uart_cfg_t uart_cfg;
uint8_t pin_cfg_count;
wm_dt_hw_pin_cfg_t *pin_cfg;
char *dma_device_name;
} wm_dt_hw_uart_t;
typedef struct {
wm_dt_hw_init_cfg_t init_cfg;
uint32_t reg_base;
wm_dt_hw_irq_cfg_t irq_cfg;
} wm_dt_hw_timer_t;
const static wm_dt_hw_pin_cfg_t dt_hw_uart0_pin[4] = {
[0] = { .pin_num = WM_GPIO_NUM_35, .pin_mux = WM_GPIO_IOMUX_FUN1, .pin_dir = WM_GPIO_DIR_INPUT, .pin_pupd = WM_GPIO_FLOAT },
[1] = { .pin_num = WM_GPIO_NUM_36, .pin_mux = WM_GPIO_IOMUX_FUN1, .pin_dir = WM_GPIO_DIR_INPUT, .pin_pupd = WM_GPIO_FLOAT },
[2] = { .pin_num = WM_GPIO_NUM_37, .pin_mux = WM_GPIO_IOMUX_FUN1, .pin_dir = WM_GPIO_DIR_INPUT, .pin_pupd = WM_GPIO_FLOAT },
[3] = { .pin_num = WM_GPIO_NUM_38, .pin_mux = WM_GPIO_IOMUX_FUN1, .pin_dir = WM_GPIO_DIR_INPUT, .pin_pupd = WM_GPIO_FLOAT },
};
const static wm_dt_hw_uart_t dt_hw_uart0 = {
.init_cfg = { .init_level = 1, .init_priority = 50 },
.reg_base = 0x40010600,
.irq_cfg = { .irq_num = WM_IRQ_UART0, .irq_priority = 0 },
.uart_cfg = { .baudrate = WM_UART_BAUDRATE_B115200,
.parity = WM_UART_PARITY_NONE,
.stop_bits = WM_UART_STOP_BIT_1,
.data_bits = WM_UART_DATA_BIT_8,
.flow_ctrl = WM_UART_FLOW_CTRL_DISABLE },
.pin_cfg_count = sizeof(dt_hw_uart0_pin) / sizeof(dt_hw_uart0_pin[0]),
.pin_cfg = (wm_dt_hw_pin_cfg_t *)dt_hw_uart0_pin,
.dma_device_name = "dma",
};
const static wm_dt_hw_uart_t dt_hw_timer0 = {
.init_cfg = { .init_level = 0, .init_priority = 50 },
.reg_base = 0x40011800,
.irq_cfg = { .irq_num = WM_IRQ_TIMER, .irq_priority = 0 },
};
C 语言配置文件中的各选项比较直观,此处不在细述含义。
备注
务必注意 pincfg
的设定,避免超过当前SOC所支持范围的定义 或 定义冲突。
比如:所定义的 GPIO_NUM 并非当前 SOC 所支持的范围,1个 pin 被多个设备定义等场景。
具体可参考 Pinmux
使用设备表
一般在使用时,用户只需使用 wm_device_t *wm_dt_get_device_by_name(const char *device_name)
从设备表中获取到设备使用即可。
typedef struct {
char *name; /**< device name */
void *hw; /**< hardware info, ref wm_dt_hw_xxx_t */
void *ops; /**< device operation interface */
void *drv; /**< driver context data */
wm_device_status_t state; /**< device state */
void *priv; /**< user private data */
} wm_device_t;
其中,hw
指针指向配置文件中的信息,由各驱动模块自行使用。
对于使用多份配置文件的场景,可通过配置文件的预设的名称进行选择使用,在代码中使用 int wm_dt_set_device_table_name(const char *default_name)
接口即可。
新增设备
可参考默认的配置文件,仿照 uart
设备添加新增设备的 hw 结构和 ops 结构即可。
在设备数据结构添加之后,更新设备表,如:
const static struct wm_dt_table_entry dt_hw_table_entry[3] = {
[0] = { .dev_name = "uart0", .hw_addr = (void *)&dt_hw_uart0, .ops_addr = (void *)&wm_drv_uart_ops },
[1] = { .dev_name = "uart1", .hw_addr = (void *)&dt_hw_uart1, .ops_addr = (void *)&wm_drv_uart_ops },
[2] = { .dev_name = "timer0", .hw_addr = (void *)&dt_hw_timer0, .ops_addr = (void *)&wm_drv_timer_ops },
};
WM_DT_TABLE_DEFINE(default, 3, (void *)&dt_hw_table_entry[0]);
其中,使用 WM_DT_TABLE_DEFINE(name, count, addr)
注册给设备表时,可以通过填写不同的名称,达到设置多份配置文件的目的。
C 语言数据结构的规则
最终使用的 C 语言数据结构中,对于每个设备的 hw 结构体有如下约定:
typedef struct {
wm_dt_hw_init_cfg_t init_cfg;
/* more ... */
} wm_dt_hw_xxx_t;
即 hw 结构体中的第一个成员必须为 init_cfg
。
init_cfg
表示该设备的初始化配置信息,将在未来版本中生效(当前版本还未实际使用)。其包含两个成员选项:init_level
表示初始化方式,取值为
1
表示由系统自动完成该设备初始化,取值为
0
表示由用户自己完成该设备初始化。init_priority
表示初始化优先级,取值为十进制整数,取值范围 0 - 100,取值越大表示优先级越高,优先级高的先初始化。相同优先级的设备在这一优先级被随机初始化。
对于每个设备的 ops 结构体有如下约定:
typedef struct {
int (*init)(wm_device_t *dev);
int (*deinit)(wm_device_t *dev);
/* more ... */
} wm_drv_xxx_ops_t;
即 ops 结构体中的前两个成员必须为 init
函数和 deinit
函数。
API 参考
可参考 DT 参考