STM32でDMA転送を使ってみる(タイマ + USART編)

出典: Wikimura

ペリフェラルはDMA要求を出しますが、要求を出したDMAが転送元・先になる必要はありません。 ペリフェラルの出すDMA要求は、ハードウェアで決まったチャネルに対して行われます。 「STM32でDMA転送を使ってみる(USART編)」で使用したUSART2の送信DMA要求は、チャネル7に対して行われます(リファレンスマニュアル参照)。


例えば、タイマ割り込みで一定間隔のDMA要求を出すことで、USARTやSPIなどから1データずつ送るというのも可能です。 (もちろん要求の間隔が短すぎて、USARTやSPIの転送が追いつかない場合はうまくいきません)


ここでは、タイマ4から1Hz「更新イベント」を発生させ、USART2へ文字を1つずつ転送させます。 文字はROMテーブルから読みだします。 DMAのモードには、一通り転送が終わったら初めに戻る「サーキュラモード」を使用します。


目次

プログラム

#include "stm32f10x.h"

void RCC_Configuration(void);
void GPIO_Configuration(void);
void USART2_Configuration( void);
void DMA1_Configuration( void);
void TIM4_Configuration( void);

static unsigned char msg[] = "DMA transfer test.\n\r";

int main(void)
{
    RCC_Configuration();
    USART2_Configuration();
    DMA1_Configuration();
    TIM4_Configuration();

    while (1)
    {
    }
}

void RCC_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    SystemInit();

    // 未使用I/Oを全てアナログ入力モードに設定
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
                            RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
                            RCC_APB2Periph_GPIOE, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
                            RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
                            RCC_APB2Periph_GPIOE, DISABLE);

}


void DMA1_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;

    //-------------------------------------------------------------------------
    // DMA1のクロック有効化
    // DMA1: AHBに属する。
    //-------------------------------------------------------------------------
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE);

    //-------------------------------------------------------------------------
    // TIM4のUpdateイベントからのDMA要求はチャネル7
    //-------------------------------------------------------------------------
    DMA_DeInit( DMA1_Channel7);                                             // 念のためクリア
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;       // USARTのデータレジスタをペリフェラル側に
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)msg;                   // メッセージをメモリ側に
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // ペリフェラルを転送先に
    DMA_InitStructure.DMA_BufferSize = sizeof(msg)/sizeof(uint8_t);         // 文字数を設定
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // ペリフェラル側インクリメントは不要
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // メモリ側インクリメント使用
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // ペリフェラル側データサイズ8ビット
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // メモリ側データサイズ8ビット
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                         // サーキュラモード使用
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 優先度: 最高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // メモリ関転送は不使用
    DMA_Init( DMA1_Channel7, &DMA_InitStructure);
    DMA_Cmd( DMA1_Channel7, ENABLE);                                        // DMA有効化
}

void USART2_Configuration( void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    //-------------------------------------------------------------------------
    // クロック有効化
    // USART用GPIO: APB2に属する。
    // USART2: APB1に属する。
    //-------------------------------------------------------------------------
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE);

    //-------------------------------------------------------------------------
    // USART2 Tx (PA.02) を alternate function push-pull に設定
    // USART2 Rx (PA.03) を alternate input floating に設定
    //-------------------------------------------------------------------------
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //-------------------------------------------------------------------------
    // USARTパラメータ設定
    // ボーレート: 9600 bps
    // データビット: 8
    // ストップビット: 1
    // パリティ: 無し
    // フロー制御: 無し
    // 使用ピン: Txのみ
    //-------------------------------------------------------------------------
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;

    //-------------------------------------------------------------------------
    // USARTxに設定を反映  + 動作有効化
    //-------------------------------------------------------------------------
    USART_Init(USART2, &USART_InitStructure);
    USART_Cmd( USART2, ENABLE);
}


void TIM4_Configuration( void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    // TIM4 クロック有効化
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4, ENABLE);

    // 1Hzのアップカウント(タイマクロック72MHz)
    TIM_TimeBaseInitStructure.TIM_Period = 9999;
    TIM_TimeBaseInitStructure.TIM_Prescaler = 7199;
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

    // 設定を反映
    TIM_TimeBaseInit( TIM4, &TIM_TimeBaseInitStructure);

    // DMA要求有効化
    TIM_DMACmd( TIM4, TIM_DMA_Update, ENABLE);

    // タイマ4起動
    TIM_Cmd( TIM4, ENABLE);
}

結果

ターミナルソフトウェアでシリアル通信を行います。以下の設定を行います。

  • ボーレート: 9600bps
  • パリティ: なし
  • データビット: 8
  • フロー制御: なし


マイコンを起動すると、メッセージ「DMA transfer test.」が延々と出力されます。 1秒に1文字出力されます。


関連記事

  1. STM32のクロック: タイマのクロックソースがどうなっているかが分かります。
  2. STM32でUSARTを使ってみる‎: USARTの最小限のプログラムです。
  3. STM32でDMA転送を使ってみる(USART編): USARTのDMA要求で連続転送を行います。
  4. STM32でタイマを使ってみる‎: タイマ割り込みでLEDを点滅させます。
  5. STM32のタイマ: ペリフェラルライブラリのタイマモジュールに関する情報をまとめています。


参考文献

  1. STM32ファミリ紹介ページ
  2. STM32関係ファイル
  3. STM32シリーズリファレンスマニュアルRM0008
  4. STM32F10x Standard Peripheral Library(3.1.0)
個人用ツール