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文字出力されます。
関連記事
- STM32のクロック: タイマのクロックソースがどうなっているかが分かります。
- STM32でUSARTを使ってみる: USARTの最小限のプログラムです。
- STM32でDMA転送を使ってみる(USART編): USARTのDMA要求で連続転送を行います。
- STM32でタイマを使ってみる: タイマ割り込みでLEDを点滅させます。
- STM32のタイマ: ペリフェラルライブラリのタイマモジュールに関する情報をまとめています。

