1.前言
最近使用中微半导的串口调试信息,由于使用的是串口1,使用的中断发送打印,中微的芯片包很贴心的给了printf的重定向,但是发送字符的函数是中断打印。
原本代码重定向函数(这里使用的是uart0,位置位于:retarget.c):
void _ttywrch(int ch)
{
UART0_Send(ch);
return;
}
int fputc(int ch, FILE *f)
{
UART0_Send(ch);
if(ch == '\n')
{
UART0_Send('\r');
}
return ch;
}
int fgetc( FILE *f)
{
uint8_t ch;
ch = UART0_Receive();
#if 1
if(ch == 0x0D) // 回车键
{
UART0_Send('\r'); // echo
UART0_Send('\n'); // echo
}
else {
UART0_Send(ch); // echo
}
#endif
return ch;
}
这里把 UART0_Send改为UART1_Send.传入单个变量,长度改为1即可。
由于原本使用的发送函数是阻塞的,但uart1是中断发送,异步发送直接换的话,会造成打印错误!所以该怎么改呢?
2.改进
1.我直接阻塞
既然你是中断打印,我直接把中断打印变成阻塞打印
volatile uint8_t uart1_send_over = 0; //定义一个flag
/***********************************************************************************************************************
* Function Name: UART1_Send
* @brief This function sends UART1 data.
* @param tx_buf - transfer buffer pointer
* @param tx_num - buffer size
* @return status - MD_OK or MD_ARGERROR
***********************************************************************************************************************/
MD_STATUS UART1_Send(uint8_t *const tx_buf, uint16_t tx_num)
{
MD_STATUS status = MD_OK;
if (tx_num < 1U)
{
status = MD_ARGERROR;
}
else
{
uart1_send_over = 1;
gp_uart1_tx_address = tx_buf;
g_uart1_tx_count = tx_num;
SCI0->TXD1 = *gp_uart1_tx_address;
gp_uart1_tx_address++;
g_uart1_tx_count--;
while(uart1_send_over );
}
return (status);
}
/***********************************************************************************************************************
* Function Name: uart1_callback_sendend
* @brief This function is a callback function when UART1 finishes transmission.
* @param None
* @return None
***********************************************************************************************************************/
static void uart1_callback_sendend(void)
{
/* Start user code. Do not edit comment generated here */
uart1_send_over = 0;
/* End user code. Do not edit comment generated here */
}
直接用uart1_send_over 标志位阻塞发送,这样就不会有问题了。
优点:简单加两三行解决问题。
缺点:没有发挥异步打印的优势。如果波特率很慢的时候,会造成响应很慢!
2.异步打印kfifo
异步打印主要解决的问题就是生产者和消费者的问题。这让我想到了kfifo,kfifo是linux一个精美且强大的队列缓存机制,既然写不出比linux更强大的代码,那我直接就用。这里不谈原理,只谈使用。
单片机由于是裸机,也没有做动态内存分配,打印串口只有一个,我直接使用静态kfifo,同时也不用考虑锁的问题,因为kfifo自身直接可以锁定单生产者消费者,简单粗暴。
kfifo使用需要声明和初始化,值得注意的是这里声明的时候需要注意大小为2的幂次方(kfifo的宏直接让编译器检查防止笨蛋输入非2的幂次方数,这就是Linux的强大)
#define KFIFO_BUFFER_SIZE 1024
DECLARE_KFIFO(fifo, uint8_t,KFIFO_BUFFER_SIZE);
void init_fifo()
{
INIT_KFIFO(fifo);
}
串口发送部分 改为
volatile uint8_t uart1_send_over = 0;
/***********************************************************************************************************************
* Function Name: UART1_Send
* @brief This function sends UART1 data.
* @param tx_buf - transfer buffer pointer
* @param tx_num - buffer size
* @return status - MD_OK or MD_ARGERROR
***********************************************************************************************************************/
MD_STATUS UART1_Send(uint8_t *const tx_buf, uint16_t tx_num)
{
uint8_t c = 0;
if(tx_num == 1)
{
kfifo_put(&fifo, *data);
}
else
{
kfifo_in(&fifo, data,len);
}
if(uart1_send_over == 0)
{
uart1_send_over = 1;
if(kfifo_get(&fifo, &c)>0)
{
SCI0->TXD1 = c;
}
}
}
/***********************************************************************************************************************
* Function Name: uart1_callback_sendend
* @brief This function is a callback function when UART1 finishes transmission.
* @param None
* @return None
***********************************************************************************************************************/
static void uart1_callback_sendend(void)
{
/* Start user code. Do not edit comment generated here */
uart1_send_over = 0;
/* End user code. Do not edit comment generated here */
}
/***********************************************************************************************************************
* Function Name: uart1_interrupt_send
* @brief UART1 Send interrupt service routine
* @param None
* @return None
***********************************************************************************************************************/
void uart1_interrupt_send(void)
{
uint8_t c;
if(kfifo_get(&fifo, &c) > 0)
{
SCI0->TXD1 = c;
}
else
{
uart1_callback_sendend();
}
}
好了,那就可以愉快的异步打印了。
优点:响应快,无阻塞,只要不超缓冲区,随便打印。
缺点:需要额外开辟内存,内存小的不要使用哦。
附带kfifo移植包(我的主页资源里)