文章目录[隐藏]
cli,主要是实现输出命令行,控制设备。再就是查询设备状态的作用。
实现过程分析
实现串口初始化
这块是由cube自动生成的。
cli任务初始化
在cli_task.c中执行module_init("cli", cli_task_init);
这样就会在程序启动初始化时,会自动调用cli_task_init();
static void cli_task_init(void)
{
cli_port_t p = {tty.write, tty.read}; /*读写接口 直接映射到底层的操作*/
cli_init(&cli, &p); /*初始化命令行对象 */
cli_enable(&cli);
cli_exec_cmd(&cli,"sysinfo"); /*找sysinfo这个命令有的话就调用里面的函数就打印出来 这个命令是在user/cmd下cmd_devinfo.c中实现的,通过cmd_regiset注册*/
}
cli_init()初始化实现
void cli_init(cli_obj_t *obj, const cli_port_t *p)
{
obj->read = p->read;
obj->write = p->write;
obj->print = cli_print;//这个是在cli函数中定义的,也就和init在同一个.c文件中,实际是实现可变参数的打印输出
obj->enable = true;
}
任务执行
除了先前的按键、灯的任务就是这个任务了。cli_process,命令处理程序,也是在cli.c中实现
void cli_process(cli_obj_t *obj)
{
int i;
if (!obj->read || !obj->enable)
return;
i = obj->recvcnt;
obj->recvcnt += obj->read(&obj->recvbuf[i], CLI_MAX_CMD_LEN - i);
while (i < obj->recvcnt) {
if (obj->recvbuf[i] == '\r' || obj->recvbuf[i] == '\n') { /*读取1行*/
obj->recvbuf[i] = '\0';
process_line(obj);//这个就是找有没有这个注册的命令,有的话就执行命令
obj->recvcnt = 0;
}
i++;
}
if (obj->recvcnt >= CLI_MAX_CMD_LEN) /*缓冲区满之后强制清空*/
obj->recvcnt = 0;
}
os默认是带了help及?两个命令的。只要串口调试器端输入help就会有反应。所以关键移植的关键还是实现初始化后的底层的读写。
串口操作
cli初始化时,tty.write()做了些什么呢?
static unsigned int uart_write(const void *buf, unsigned int len)
{
unsigned int ret;
ret = ring_buf_put(&rbsend, (unsigned char *)buf, len); //将数据放到发送缓冲里,由环形队列实现
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//启动发送中断
return ret;
}
从这里可以分析到:应该是先由串口调试器,发送命令给OS,os一看有这个命令,便会跳到这个命令函数处,执行里面的内容。命令函数里便会调用cli_print(),调用这里的write,发送相应的内容。
tty.read();//更简单了,只是ring_buf_get(&rbrecv, (unsigned char *)buf, len);
//从缓冲区拿数据就可以了。
串口中断的实现
void USART1_IRQHandler(void)
{
unsigned char data;
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
data = USART_ReceiveData(USART1);
ring_buf_put(&rbrecv, &data, 1); /*按字节接收数据,将数据放入接收缓冲区*/
}
if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) {
if (ring_buf_get(&rbsend, &data, 1)) /*从缓冲区中取出数据---*/
USART_SendData(USART1, data); //发送数据
else{
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
if (USART_GetITStatus(USART1, USART_IT_ORE_RX) != RESET) {
data = USART_ReceiveData(USART1);
}
}
hal库串口操作
关于hal阻塞式发送和接收函数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);//串口发送,发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);//串口接收,接收指定长度的数据。如果超时没接收完成,则不再接收数据到指定缓冲区,返回超时标志(HAL_TIMEOUT)。
发送就等着发送完,HAL中阻塞式发送函数的第4个参数Timeout,可以设置一个超时时间,超时后没发完就不再阻塞。
对应的就是标准库里的
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
中断式发送与接收函数 所谓的非阻塞式
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口中断发送,以中断方式发送指定长度的数据。
大致过程是,把 发送缓冲区指针 指向 要发送的数据,设置 发送长度,发送计数器初值,然后使能串口发送中断,触发串口中断。
再然后,串口中断函数处理,直到数据发送完成,而后关闭中断,不再发送数据,串口发送完成回调函数。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);//串口中断接收,以中断方式接收指定长度数据。
大致过程是,把 接收缓冲区指针 指向 要存放接收数据的数组,设置 接收长度,接收计数器初值,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,不再触发接收中断,调用串口接收完成回调函数。
在HAL中,打开串口和中断合并成了一个函数:发送就打开发送中断,接收就打开接收中断
发送数据
串口初始化完成后,
使能USARTx_CR1的TE位使能发送器。
HAL_UART_Init(UART_HandleTypeDef *huart) 调用以下
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
实现的操作为:
- 向发送数据寄存器TDR写入要发送的数据(对于M3,发送和接收共用DR寄存器)。
- 向TRD寄存器写入最后一个数据后,等待状态寄存器USARTx_SR(ISR)的TC位置1,传输完成。这种状态属于阻塞式发送
接收数据
初始化完成后,使能USARTx_CR1的RE位为1使能接收器。
如果要使能接收中断(接收到数据后产生中断),使能USARTx_CR1的RXNEIE位为1。
HAL_UART_Init(UART_HandleTypeDef \*huart)
/* 配置7步骤:开启接收中断,并且设置接收缓冲剂最大接收数据量 */
HAL_UART_Receive_IT(UART_HandleTypeDef \*huart, uint8_t *pData, uint16_t Size)
串口接收到数据时
USARTx_SR(ISR)的RXNE位置1。表明移位寄存器内容已经传输到RDR(DR)寄存器。已经接收到数据并且等待读取。
如果开启了接收数据中断(USARTx_CR1寄存器的RXNEIE位为1),则会产生中断。(程序上会执行中断服务函数)
如果开启了其他中断(帧错误等),相应标志位会置1。
读取USARTx_TDR(DR)寄存器的值,该操作会自动将RXNE位清零,等待下次接收后置位。
步骤为:
/* 步骤1 ,获取状态标志位通过标识符实现 */
__HAL_UART_GET_FLAG //判断状态标志位
__HAL_UART_GET_IT_SOURCE //判断中断标志位
/* 步骤2~3,中断服务函数 */
void USARTx_IRQHandler(void) ;
/* 步骤4,读取接收数据 */
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
网友总结的已经很好了:
在HAL中void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);这个库函数帮我们完成了中断类型判断和清除标志位,我们只需要在具体的函数中写逻辑即可。
扩展 关于串口的DMA方式相关函数
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
串口DMA发送,以DMA方式发送指定长度的数据。
过程是,把 发送缓冲区指针 指向 要发送的数据,设置 发送长度,发送计数器初值,设置 DMA传输完成中断的回调函数,使能DMA控制器中断,使能DMA控制器传输,使能UART的DMA传输请求。
然后,UART便会发送数据,直到发送完成,触发DMA中断。
DMA中断处理,如果 DMA模式 是 循环模式,则 直接 调用 DMA传输完成中断的回调函数。
如果 DMA模式 是 正常模式,则 先 关闭DMA传输完成中断,不再触发DMA中断,再 调用 DMA传输完成中断的回调函数。
DMA传输完成中断的回调函数处理过程,如果 DMA模式 是 循环模式,则 直接 调用 串口发送完成回调函数。
如果 DMA模式 是 正常模式,则 先关闭 UART的DMA传输请求, 再 使能串口传输完成中断,直到传输完成,触发中断。
串口传输完成中断处理,关闭中断,调用串口发送完成回调函数。
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
串口DMA接收,以DMA方式接收指定长度的数据。
过程是,把 接收缓冲区指针 指向 要存放接收数据的数组,设置 接收长度,接收计数器初值,设置 DMA传输完成中断的回调函数,使能DMA控制器中断,使能DMA控制器传输,使能UART的DMA传输请求。
然后,UART接收到数据,便会通过DMA把数据存到接收缓冲区,直到接收到指定长度数据,触发DMA中断。
DMA中断处理,如果 DMA模式 是 循环模式,则 直接 调用 DMA传输完成中断的回调函数。
如果 DMA模式 是 正常模式,则 先 关闭DMA传输完成中断,不再触发DMA中断,再 调用 DMA传输完成中断的回调函数。
DMA传输完成中断的回调函数处理过程,如果 DMA模式 是 循环模式,则 直接 调用 串口接收完成回调函数。
如果 DMA模式 是 正常模式,则 先关闭 UART的DMA传输请求, 再 调用 串口接收完成回调函数。
模式,则 先 关闭DMA传输完成中断,不再触发DMA中断,再 调用 DMA传输完成中断的回调函数。
DMA传输完成中断的回调函数处理过程,如果 DMA模式 是 循环模式,则 直接 调用 串口接收完成回调函数。
版权声明:本文为CSDN博主「guangod」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guangod/article/details/121760991
暂无评论