STM32F1のUSARTとDMAを組み合わせてみました
今回は受信をDMAで転送し、送信はソフトウェアループで行いました
プログラムですが
まずFIFOとしてメモリを確保します
unsigned char ConsoleRxBuffer[100];
次に初期化です
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2, &USART_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DR);
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(ConsoleRxBuffer);
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0]);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel6, ENABLE);
USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART2, ENABLE);
GPIO USART DMAを初期化します
RCCは予め初期化しておく必要があります
このコードで使用するのはDMA1 USART2 GPIOAです
今回は受信割り込み等は使用しないため、NVICの初期化は不要です
上記3種類の初期化が終了した段階でUSARTの送信が可能であり、データを受信した場合はFIFOに追加されます
最後にFIFOの解析です
const unsigned char *ConsoleInputStr(void) {
static unsigned char strBuffer[50];
static uint16_t BufferPos = 0, FIFOpos = 0;
uint16_t now = (sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0])) - DMA_GetCurrDataCounter(DMA1_Channel6);
while (FIFOpos != now) {
char ch = ConsoleRxBuffer[FIFOpos++];
if (FIFOpos == (sizeof(ConsoleRxBuffer) / sizeof(ConsoleRxBuffer[0]))) { FIFOpos = 0; }
if (ch >= ' ' && ch <= '~') {
strBuffer[BufferPos++] = ch;
} else if (ch == '\n') {
strBuffer[BufferPos] = 0;
BufferPos = 0;
return(strBuffer);
}
}
return(0);
}
この関数によりFIFOから1行を受信します
FIFOに改行コードが含まれない場合はぬるぽを返し、改行コードを発見した場合は文字列バッファのポインタを返します
文字列は次にこの関数が呼び出されるまで保証されます
今回はバッファオーバーラン対策はしていません
DMAを使用することによりUSARTの受信→バッファへの転送はハードウェアで行われます
そのためある程度高速でデータを送り込まれても(FIFOの容量までは)取りこぼしせずに受信することができます
STM32F1は115.2kbaudや921.6kbaud等の高速な通信ができますが、byte/secが増える毎に割り込みのオーバーヘッドが無視できなくなります
また、下位の割り込みへのタイミング精度や、上位の割り込みによるデータの取りこぼしの問題も出てきます
その点受信をDMAで行うとそれらの懸念を減らすことができます
依然USARTの送信はプログラムループか、ソフトウェア割り込みで行う必要がありますが、送信については任意のタイミングで行えるため、上位の割り込みによる影響などはあまりありません
またUSARTの割り込み優先度を下げることができるので、他の割り込みへの影響も減らせます
ある程度の速度でデータがどんどん流れこんでくる用途 例えばUART接続のGPSモジュール等に使った場合に有利だと思います
0 件のコメント:
コメントを投稿