2014年7月3日木曜日

STM32F1のUSARTとDMA

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 件のコメント:

コメントを投稿