上文中提到单片机中竟然能用printf函数,一般这种输出打印格式在上位机软件开发调试中会经常使用到。众所周知,嵌入式软件就是数据流的控制。在单片机开发中使用printf函数进行数据流的观测极大方便开发人员调试,在不进行断点调试中,对关键点的信息进行及时的输出,方便开发人员知道信息数据流的流动情况和数据是否正确。下面讲解如何在单片机中使用printf函数。
第一步,需要将单片机的USART调试成功并且初始化,然后将串口的发送和接收函数重构如下:
1//发送字符串 2int fputc(int ch, FILE *f) 3{ 4 5 USART_SendData(USART1, (u8) ch); 6 7 8 while(!(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == SET)) 9 {10 }1112 return ch;13}14/*接收一个字节数据*/15int fgetc(FILE *f)16{1718 while(!(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET))19 {20 }2122 return (USART_ReceiveData(USART1));23}
第二步,打开KEIL工程中的Project->Options for Target '***',选项卡如下,勾选上 "Use MicroLIB",便能够实现单片机通过串口实现printf函数。microlib 是缺省 C 库的备选库。它旨在与需要装入到极少量内存中的深层嵌入式应用程序配合使用。
![48222ce35730ac691799f8d6f6f6f1b7.png](https://i-blog.csdnimg.cn/blog_migrate/3a7ad3db6b13a5ec5ec35ca6b56cb249.png)
下面讲解下采用FIFO机制下的队列数据的写入和读出。为了实现任务A和任务B通讯,创建了一个可以容纳5个整数的队列,队列在创建时不包含任何数据,所以队列是空的如图1所示。
![ff862fe87774532b58ec255ed697e112.png](https://i-blog.csdnimg.cn/blog_migrate/efec65dde41e4fca61c3124243b56a50.png)
图 1 创建任务
任务A向队列中写入x的值,由于创建队列时队列为空,写入的10既在队列的尾也在队列的首。因为FreeRTOS采用的是值传递,所以当10写入队列中后,x可以再次被使用,x值改为20,再次向队列中写入,因此队列中包含两个数值,10在队列首部,20在队列尾部如图2 所示
![42ee585a45dda771b52e915fc098cdde.png](https://i-blog.csdnimg.cn/blog_migrate/7219c0d6603301e5e7dc2ce71494062d.png)
![96e40b2cde71247ad1e85e5faf089244.png](https://i-blog.csdnimg.cn/blog_migrate/0f186895edf6e7c65b80685206256016.png)
图 2 写入队列
任务B从队列中读取一个数值,首先位于队列首部的10会被任务B读取,赋值给y,既y=10,读取完成后,10将会被移除队列,20将会移动到10的位置,即队列首部,此时队列中剩余四个空位。如图3所示。
![1c4313a3a1e286f4495b84b106e6fd99.png](https://i-blog.csdnimg.cn/blog_migrate/b2552a2c4ae94f1dc6d3fbcfd0301ee1.png)
![517c7ce11461e731f50b2e95c2071b7a.png](https://i-blog.csdnimg.cn/blog_migrate/dc441b0be6ccff21183d35e7d3b9705f.png)
图 3 读队列、
在进行入队和出队操作时,FreeRTOS支持“入队阻塞”和“出队阻塞”,下面讲解下入队阻塞,当任务A向队列发送消息时,此时若队列满,此时任务A向队列写入不了任何数据,FreeRTOS应对此种情况提供了三种方式:
- 不等待,立马执行接下来的代码。
- 等待一定时间,在等待时间内如果队列空,则任务跳转至就绪态,若超过等待时间,自动转为就绪态。
- “死等”,一直等,直到队列为空。
创建两个任务,一个串口接收中断,串口接收中断接收数值后写入串口队列中;任务1向数值队列中写入定数值;任务二打印串口中断中写入串口队列中的数值,打印任务一种写入数值队列中的数值。代码如下:
串口中断中向串口队列写入数值:
1void USART1_IRQHandler(void) 2{ 3 BaseType_t xHigherPriorityTaskWoken; 4 static uint8_t Res=0; 5 xHigherPriorityTaskWoken = pdFALSE; 6 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 7 { 8 RXbuffer[Res] =USART_ReceiveData(USART1); //读取接收到的数据 9 Res++; 10 if(Res ==ConfigUASRTRxLength)11 Res =0;12 printf("Res is %d\r\n",Res);13//向串口队列中写入数值 14 xQueueSendFromISR( USART_Handler, &RXbuffer, &xHigherPriorityTaskWoken ); 15 USART_ClearITPendingBit(USART1, USART_IT_RXNE);// 清除中断标志16 USART_ClearFlag(USART1,USART_IT_RXNE); //一定要清除接收中断 17 portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); 18 }19//溢出-如果发生溢出需要先读SR,再读DR寄存器则可清除不断入中断的问题20 if(USART_GetFlagStatus(USART1,USART_FLAG_ORE) == SET)21 {22 USART_ReceiveData(USART1);23 USART_ClearFlag(USART1,USART_FLAG_ORE);24 } 25}
任务1向数值队列中写入数值,任务2打印两个队列中的数值。
1//task1 向数值数列中写入定值 2void task1_task(void *pvParameters) 3{ 4 uint8_t temp=10; 5 while(1) 6 { 7 if( xQueueSend( KEY_Q_Handler,&temp,10 )!=pdTRUE) 8 { 9 printf("send to queue is error,queue is full\n\r");10 }else11 {12 printf("senddata is %d\n\r",temp);13 }14 vTaskDelay(1000); 15 }16} 17//task2 读取两个队列中的数值,并打印18void task2_task(void *pvParameters)19{20 uint8_t recdata;21 uint8_t buffer[ConfigUASRTRxLength],u;22 while(1)23 {24 if( xQueueReceive( KEY_Q_Handler, &recdata, 10 ) != pdPASS )25 {26 printf("receive queue is error,queue is empty\n\r");27 }else28 {29 printf("recedata is %d\n\r",recdata);30 }3132 if( xQueueReceive( USART_Handler, &buffer, 10 ) != pdPASS )33 {34 //printf("receive queue is error,queue is empty\n\r");35 }else36 {37 for(u=0;u38 printf("buffer is %c\n\r",buffer[u]);39 }4041 vTaskDelay(1000);42 }43}
3、测试
通过串口调试软件向下发送1~5,串口中断接收到后写入队列,在任务2中打印出来,任务2同时打印任务1向数值中断中写入的10,测试结果如下: