引脚简介
芯片上的引脚一般分为四类:电源、时钟、控制与I/O。
I/O口在使用模式上又分为General Purpose Input Output(通用输入/输出),简称GPIO,与功能复用I/O(如SPI/I2C/UART等)。
大多数MCU的引脚都不止一个功能。不同引脚内部结构不一样,拥有的功能也不一样。可以通过不同的配置,切换引脚的实际功能。
- 可编程控制中断
- 输出模式一般包括:推挽、开漏、上拉、下拉。引脚为输出模式时,可以通过配置引脚输出的电平状态为高电平或低电平来控制连接的外围设备。
- 输入模式一般包括:浮空、上拉、下拉、模拟。引脚为输入模式时,可以读取引脚的电平状态,即高电平或低电平。
在pin.c硬件框架中
void rt_pin_write(rt_base_t pin, rt_ssize_t value)
{RT_ASSERT(_hw_pin.ops != RT_NULL);_hw_pin.ops->pin_write(&_hw_pin.parent, pin, value);
}
static struct rt_device_pin _hw_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_pin_ops
{void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);rt_ssize_t (*pin_read)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_base_t pin,rt_uint8_t mode, void (*hdr)(void *args), void *args);rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled);rt_base_t (*pin_get)(const char *name);
#ifdef RT_USING_DMrt_err_t (*pin_irq_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);rt_ssize_t (*pin_parse)(struct rt_device *device, struct rt_ofw_cell_args *args, rt_uint32_t *flags);
#endif
#ifdef RT_USING_PINCTRLrt_err_t (*pin_ctrl_confs_apply)(struct rt_device *device, void *fw_conf_np);
#endif /* RT_USING_PINCTRL */
};
在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_Pin;_hw_pin.parent.rx_indicate = RT_NULL;_hw_pin.parent.tx_complete = RT_NULL;#ifdef RT_USING_DEVICE_OPS_hw_pin.parent.ops = &pin_ops;
#else_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;
#endif_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;
}
在设备驱动程序中,就会去调用这个函数
return rt_device_pin_register("pin", &_stm32_pin_ops, RT_NULL);
调用之前需要定义,初始化_stm32_pin_ops
static const struct rt_pin_ops _stm32_pin_ops =
{stm32_pin_mode,stm32_pin_write,stm32_pin_read,stm32_pin_attach_irq,stm32_pin_dettach_irq,stm32_pin_irq_enable,stm32_pin_get,
};
static const struct rt_pin_ops _stm32_pin_ops =
{stm32_pin_mode,stm32_pin_write,stm32_pin_read,stm32_pin_attach_irq,stm32_pin_dettach_irq,stm32_pin_irq_enable,stm32_pin_get,
};
struct rt_pin_ops
{void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);rt_ssize_t (*pin_read)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_base_t pin,rt_uint8_t mode, void (*hdr)(void *args), void *args);rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin);rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled);rt_base_t (*pin_get)(const char *name);
#ifdef RT_USING_DMrt_err_t (*pin_irq_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);rt_ssize_t (*pin_parse)(struct rt_device *device, struct rt_ofw_cell_args *args, rt_uint32_t *flags);
#endif
#ifdef RT_USING_PINCTRLrt_err_t (*pin_ctrl_confs_apply)(struct rt_device *device, void *fw_conf_np);
#endif /* RT_USING_PINCTRL */
};
static rt_base_t stm32_pin_get(const char *name)
{rt_base_t pin = 0;int hw_port_num, hw_pin_num = 0;int i, name_len;name_len = rt_strlen(name);if ((name_len < 4) || (name_len >= 6)){goto out;}if ((name[0] != 'P') || (name[2] != '.')){goto out;}if ((name[1] >= 'A') && (name[1] <= 'Z')){hw_port_num = (int)(name[1] - 'A');}else{goto out;}for (i = 3; i < name_len; i++){hw_pin_num *= 10;hw_pin_num += name[i] - '0';}pin = PIN_NUM(hw_port_num, hw_pin_num);return pin;out:rt_kprintf("Px.y x:A~Z y:0-15, e.g. PA.0\n");return -RT_EINVAL;
}
UART简介
UART(Universal Asynchronous Receiver/transmitter)通用异步收发传输器,UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。
UART 串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示:
起始位:表示数据传输的开始,电平逻辑为 “0” 。
数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,因为一个 ASCII 字符值为 8 位。
奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。
停止位: 表示一帧数据的结束。电平逻辑为 “1”。
波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为每秒比特数 bit/s(bps)。常见的波特率值有 4800、9600、14400、38400、115200等,数值越大数据传输的越快,波特率为 115200 表示每秒钟传输 115200 位数据。
通过设备句柄,应用程序可以打开和关闭设备,打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
#define RT_DEVICE_FLAG_STREAM 0x040 /* 流模式 */
/* 接收模式参数 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中断接收模式 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接收模式 */
/* 发送模式参数 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中断发送模式 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发送模式 */
串口数据接收和发送数据的模式分为三种:中断模式、轮询模式、DMA模式。
在使用的时候,这3种模式只能选其一,若串口的打开参数oflags没有指定使用中断模式或者DMA模式,则默认使用轮询模式。
中断处理方式那样保留现场和恢复现场的过程,通过 DMA 控制器为 RAM 与 I/O 设备开辟一条直接传送数据的通路,这就节省了 CPU 的资源来做其他操作。使用 DMA 传输可以连续获取或发送一段信息而不占用中断或延时,在通信频繁或有大段信息要传输时非常有用。
流模式 RT_DEVICE_FLAG_STREAM 可以和接收发送模式参数使用或 “|” 运算符一起使用。
通过控制接口,应用程序可以对串口设备进行配置,如波特率、数据位、校验位、接收缓冲区大小、停止位等参数的修改。
接收缓冲区:当串口使用中断接收模式打开时,串口驱动框架会根据RT_SERIAL_RB_BUFSIZE大小开辟一块缓冲区用于保存接收到的数据,底层驱动接收到一个数据,都会在中断服务程序里面将数据放入缓冲区。
默认串口配置接收数据缓冲区大小为 RT_SERIAL_RB_BUFSZ,即 64 字节。
若一次性数据接收字节数很多,没有及时读取数据,那么缓冲区的数据将会被新接收到的数据覆盖,造成数据丢失,建议调大缓冲区,即通过 control 接口修改。在修改缓冲区大小时请注意,缓冲区大小无法动态改变,只有在 open 设备之前可以配置。open 设备之后,缓冲区大小不可再进行更改。但除缓冲区之外的其他参数,在 open 设备前 / 后,均可进行更改。