前回の記事でUARTでのprintfの速度は実装しだいと書いた。
UARTの送信をブロッキング処理でおこなうと相応の時間を要するが、バッファにためて割り込みやDMAで送信すればprintf関数の処理時間を短くすることができる。ただし、当然ながら通信のスループットはUARTのボーレートで決まる。
ブロッキング処理でのUART printf
たとえばMCUXpressoでLPCOpenプロジェクトの場合、下記のようなソースでprintf関数の出力をUARTに送信できる。実験にはLPC1857マイコンを使用した。ボーレートは115200。
#include <stdio.h>
#include "board.h"
void SysTick_Handler(void)
{
static int cnt = 0;
static int x = 0;
if (x++ > 1000) {
x = 0;
cnt++;
Board_LED_Set(0, false);
printf("Count up! %d\n", cnt);
Board_LED_Set(0, true);
}
}
int main(void)
{
SystemCoreClockUpdate();
Board_Init();
Board_Debug_Init();
SysTick_Config(SystemCoreClock / 1000);
printf("Hello, world!\n");
while(1) {
;
}
return 0 ;
}
このときprintf関数で10文字送信するのに要する時間は約710usecであった。ボーレート115200で1文字送信するのに要する時間は86.8usecであるから、これは(10-2)文字送信する時間 + printf関数の書式処理時間とみられる。-2文字はUARTの送信データレジスタと送信シフトレジスタのぶんである。
このように、セミホスティングよりマシではあるが、IMTやRTTに比べるとprintf関数の所要時間が長い。IMTやRTTが利用できるならそちらを検討したほうが良いだろう。
割り込み処理による改善
ただし、どうしてもUARTにログを出したい場合もあるだろう。その場合には送信データをリングバッファにためて割り込み処理で送信すればprintf関数の所要時間を短縮できる。たとえばMCUXpressoでLPCOpenプロジェクトの場合、下記のような修正でUART printfを割り込み処理にできる。
(1) デバッグUARTの割り込み番号と割り込みハンドラ名を定義
ボードライブラリのboard.hに下記のような定義を追加する。ただし、UART番号はターゲットの基板に合わせること。
#define DEBUG_UART LPC_USART3
#define DEBUG_UART_IRQn USART3_IRQn
#define DEBUG_UART_IRQHandler UART3_IRQHandler
(2) デバッグUART用のリングバッファと割り込みハンドラを定義
ボードライブラリのboard.cに下記の定義を追加する。バッファサイズは適宜調整すること。
#define UART_RB_SIZE 256
static uint8_t txbuff[UART_RB_SIZE], rxbuff[UART_RB_SIZE];
static RINGBUFF_T txring, rxring;
void DEBUG_UART_IRQHandler(void)
{
Chip_UART_IRQRBHandler(DEBUG_UART, &rxring, &txring);
}
(3) Board_Debug_Init関数を修正
board.cのBoard_Debug_Init関数を下記のように修正する。
void Board_Debug_Init(void)
{
#if defined(DEBUG_UART)
Board_UART_Init(DEBUG_UART);
Chip_UART_Init(DEBUG_UART);
Chip_UART_SetBaud(DEBUG_UART, 115200);
Chip_UART_ConfigData(DEBUG_UART, UART_LCR_WLEN8 | UART_LCR_SBS_1BIT | UART_LCR_PARITY_DIS);
Chip_UART_TXEnable(DEBUG_UART);
RingBuffer_Init(&txring, txbuff, 1, UART_RB_SIZE);
RingBuffer_Init(&rxring, rxbuff, 1, UART_RB_SIZE);
Chip_UART_IntEnable(DEBUG_UART, UART_IER_RBRINT);
NVIC_SetPriority(USART3_IRQn, 1);
NVIC_EnableIRQ(USART3_IRQn);
#endif
}
(4) Board_UARTPutChar関数を修正
board.cのBoard_UARTPutChar関数を下記のように修正する。
void Board_UARTPutChar(char ch)
{
#if defined(DEBUG_UART)
Chip_UART_SendRB(DEBUG_UART, &txring, &ch, 1);
#endif
}
以上の変更を施したところ、10文字送信するprintf関数の処理時間は710usecから80usecに短縮された。ただし最初に述べたように、UARTの送信時間じたいはボーレートで決まり、約870usec(86.8usec×10文字)かかることに留意されたい。