01 TIM3のPWMモードでパルス発生 (TIM3,GPIO,ピン配置,AF,Remap) (割り込みなし)

まず最初に、TIM3のPWMモードでパルスを発生させてみます.

この行為には、TIM3だけでなく、GPIO、ピン配置、Alternate Function、Remapというキーワードも関連します.

割り込みはまだ使っていません.

タイマ動作の基礎知識

タイマにはclockをカウントする16bitレジスタがあります.

この16bitレジスタは0を起点にカウントアップするわけですが、上限をたとえば999などという任意の数値に設定できます.

すると、下図のようにclockを1/1000にした周期で何か仕事をさせられます.

↓下図はPWMの仕事をさせる原理図でして、300をthreshold levelに設定しておくとduty30%の信号を出力できます.

↓他にもカウントダウンさせる使い方もできます.(下図) この例ではclockを1/5000に分周しています.

カウンタがゼロに戻る時に、割り込みを発生させることもできます.

↓あまり使わないと思いますが、下図のようにup/downさせるカウンタの動かし方も可能です.

clockを分周すると書きましたが、プリスケーラという機能もあります.プリスケーラとは、clockをN分周してからカウンタを駆動する機能です.

16bitレジスタですから、最大でも65536分周めでしかできませんが、プリスケーラと組み合わせることで最大65536・N分周に拡大できます.

サンプルプログラム

このページの末尾からDLできます.解凍するとつぎのproject folderが丸ごと現れます.

binには、buildで生成されSTM32-DISCOVERYに焼く.hexファイルが格納されます.

Librariesには、STM社のライブラリが丸ごとコピーされています.

見慣れない128_8.ldはリンカのスクリプトです.

makefileは、eclipsにてbuildの全てを司るファイルです.

project folderをeclipseに組み込む方法

①サンプルプログラムを解凍してどこかに置きます (eclipseと関係ないフォルダに置いてください)

②Project Exprolerで右クリック→Importをクリック

③Import windowが開きます.Existing Project into Workspaceを選択し、Nextをクリック

④Select root directoryのボタンをクリックし、その右にBlowseをクリック

⑤解凍しておいたstdperiph_01_TIM_PWMOUTフォルダを選択し、OKをクリック

⑥Copy projects into workspaceをチェック 忘れずに!

⑦Finishをクリック

⑧make allが自動的に起動されます.makeがBuildの必要性を察知したのならBuildされます.

不要な中間ファイルを消去する方法

右側にあるMake Targetペインのall_cleanをダブルクリックします

Buildする方法

右側にあるMake Targetペインのallをダブルクリックします

どんな動作をするか?

TIM3の出力は、TIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4の4本があります.

ピン配置は、一意に決まるのではなく、2者択一なので後で説明します. (Alternate Function、Remapのことです)

出力周波数は36kHzです.

4本の出力の違いはdutyです.

TIM3_CH1 50%

TIM3_CH2 37.5%

TIM3_CH3 25%

TIM3_CH4 12.5%

source code

main.c

#include "stm32f10x.h" CPUのハードウエアによって決まる割り込み番号やレジスタアドレスを定義しています

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM3を設定するためだけに必要な構造体です

TIM_OCInitTypeDef TIM_OCInitStructure; TIM3を設定するためだけに必要な構造体です

void RCC_Configuration(void); resetとclockの初期設定のための関数です

void GPIO_Configuration(void); GPIOの初期設定のための関数です

int main(void)

{

RCC_Configuration(); clockの初期設定をしてます.下の方にcodeがあります.

GPIO_Configuration(); GPIOの初期設定をしてます.下の方にcodeがあります.

↓ここからがいよいよTIM3の設定です.

clockについて特段の設定をしていないこの状況では、TIM3のclockは24MHzです.

プリスケ倍率は1です.カウンタは0~665を回ります.すると36kHzになります.

// Time base configuration 24MHz / 1 / 666 = 36kHz

TIM_TimeBaseStructure.TIM_Period = 665; // count 0-665

TIM_TimeBaseStructure.TIM_Prescaler = 0; // presc=1

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM_Periodは、16bitの数値です.カウンタの最大値を設定します.

TIM_Prescalerは、16bitの数値です.プリスケ値を設定します.

TIM_ClockDivisionは、外部信号をサンプルする周期fDTSを次のように決めます.外部信号を受信するわけではないので0でOKです.

0: fDTS = 24MHz

1: fDTS = 12MHz

2: fDTS = 6MHz

TIM_CounterModeは、次の選択肢があります.

TIM_CounterMode_Up カウントアップモード

TIM_CounterMode_Down カウントダウンモード

TIM_CounterMode_CenterAligned1 カウントアップダウンモード(割り込みは谷で発行する)

TIM_CounterMode_CenterAligned2 カウントアップダウンモード(割り込みは山で発行する)

TIM_CounterMode_CenterAligned3 カウントアップダウンモード(割り込みは山谷両方で発行する)

TIM_TimeBaseInit()の第一引数でTIM3を指定する.第二引数で構造体を指定する.構造体の情報をTIM3に設定します.

←これはCenterAligned3

↓ここから下は、PWMの設定です.最初にTIM3_CH1の設定.

カウンタが0-666まで回る条件下で、しきい値333でLOW/HIGHを切り替えるので50% dutyのパルスが出ます.

// PWM1 Mode configuration: TIM3_CH1 333 / 666 = 50% duty

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 333;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

TIM_OC1Init(TIM3, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

TIM_OCModeは次の選択肢があります.

TIM_OCMode_PWM1 PWMモード

TIM_OCMode_PWM2 PWMモード(逆極性)

TIM_OCMode_Active カウンタ=しきい値になった瞬間、TIM3_CH1にHIGHパルスを出すらしい

TIM_OCMode_Inactive カウンタ=しきい値になった瞬間、TIM3_CH1にLOWパルスを出すらしい

TIM_OCMode_Timing 比較動作はしないでカウンタだけがクルクル回るらしいがよくわからない

TIM_OCMode_Toggle カウンタ=しきい値になったら、TIM3_CH1を反転させるらしい

TIM_OutputStateの選択肢は、TIM_OutputState_DisableまたはTIM_OutputState_Enableです.

TIM_PulseはPWMの比較値です.16bitです.

TIM_OCPolarityの選択肢は、TIM_OCPolarity_HighまたはTIM_OCPolarity_Lowです.

TIM_OC1Init()はSTMライブラリ関数です.第一引数にTIM3を指定し、第二引数に構造体を指定します.構造体の情報をTIM3に設定します.

TIM_OC1PreloadConfig()はSTMライブラリ関数です.TIM3内部回路において各種設定値が採用される時刻を決めているようです.

TIM_OCPreload_Enable カウンタがmax/minにドンついたときに採用 (通常はこちらでしょう)

TIM_OCPreload_Disable 無手順的に採用 (一瞬動作がおかしくなるかも)

↓TIM3_CH2の設定.

カウンタが0-666まで回る条件下で、しきい値249でLOW/HIGHを切り替えるので37.5% dutyのパルスが出ます.

// PWM1 Mode configuration: TIM3_CH2 249 / 666 = 37.5% duty

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 249;

TIM_OC2Init(TIM3, &TIM_OCInitStructure);

TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

↓TIM3_CH3の設定.

カウンタが0-666まで回る条件下で、しきい値166でLOW/HIGHを切り替えるので25% dutyのパルスが出ます.

// PWM1 Mode configuration: TIM3_CH3 166 / 666 = 25% duty

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 166;

TIM_OC3Init(TIM3, &TIM_OCInitStructure);

TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);

↓TIM3_CH4の設定.

カウンタが0-666まで回る条件下で、しきい値83でLOW/HIGHを切り替えるので12.5% dutyのパルスが出ます.

// PWM1 Mode configuration: TIM3_CH4 83 / 666 = 12.5% duty

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 83;

TIM_OC4Init(TIM3, &TIM_OCInitStructure);

TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);

↓STMライブラリ関数です.ARRはカウンタのmax値のことです.動け!と命じているんでしょう.

TIM_ARRPreloadConfig(TIM3, ENABLE);

↓STMライブラリ関数です.TIM3に動け!と命じているんでしょう.

TIM_Cmd(TIM3, ENABLE); // TIM3 enable counter

while(1){;} 永久ループ

}

↓reset/clockの設定をします.ここで設定しているAPBって何かを理解する必要があります.

void RCC_Configuration(void)

{

/* TIM3 clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

/* GPIOA and GPIOB clock enable */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |

RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

}

下図はSTM32のブロック図です.左上にあるCoretex-M3がCPUです.ARMのCPUの一種なんでしょう.

右下にあるTIM3がこのプログラムでの制御対象です.それとGPIOも制御対象です.

TIM3へAPB1という矢印が行ってます.GPIOへAPB2という矢印が行ってます.

このAPBがAPBバスというバス規格の一種です.細かい仕様は私は知りません.

それで、TIM3へ供給されるclockはAPB1バス信号として供給されますので、RCC_APB1PeriphClockCmd()でclockをenableしてます.

GPIOへ供給されるclockはAPB2バス信号として供給されますので、RCC_APB2PeriphClockCmd()でclockをenableします.

disable状態のperipheralはclockが供給されないので省電力の役に立ちます.

第一引数の選択肢は下記です.

RCC_APB1PeriphClockCmd()

RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7, RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2, RCC_APB1Periph_PWR, RCC_APB1Periph_DAC, RCC_APB1Periph_CEC

RCC_APB2PeriphClockCmd()

RCC_APB2Periph_AFIO, RCC_APB2Periph_GPIOA, RCC_APB2Periph_GPIOB, RCC_APB2Periph_GPIOC, RCC_APB2Periph_GPIOD, RCC_APB2Periph_GPIOE, RCC_APB2Periph_GPIOF, RCC_APB2Periph_GPIOG, RCC_APB2Periph_ADC1, RCC_APB2Periph_TIM1, RCC_APB2Periph_SPI1, RCC_APB2Periph_USART1, RCC_APB2Periph_TIM15, RCC_APB2Periph_TIM16, RCC_APB2Periph_TIM17

↓GPIO_Configuration()は、GPIOの初期設定をしています.

ここでGPIOを設定するとは、TIM3の出力すなわちTIM3_CH1,TIM3_CH2,TIM3_CH3,TIM3_CH4をSTM32のどこかのピンに出力するように設定することに他なりません.

しかしTIM3_CHxをSTM32の任意のピンに設定できるわけではありません.あらかじめどこそものピンと決まっています.

どこのピンかを知るにはどの資料を参照すればいいのでしょうか?

それが下記の資料です.24ページのtable4を見て下さい.

http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00251732.pdf

TIM3_CHxが登場する場所を抜粋しました.

table4の見方を説明します.

・STM32-DISCOVERYに載っているのはLQFP64ですのでPins列はLQFP64の列に着目します.

・Pin name列にはPC6などの名前が書かれています.これはGPIOC-bit6の意味です.

・Main functionの列は、起動後に何も手を下さなければこの機能がアサインされます.

・Default列は、peripheralのIOとしてアサインしたときの機能が書かれています.22,23,26,27pinにTIM3_CHxがアサインされていることがわかります.

・Remap列も、peripheralのIOとしてアサインしたときの機能が書かれています.Remapすると63,64,65,66pinにTIM3_CHxのアサインが移動します.

↓という知識を元に、GPIO_Configuration()を眺めてみます.

void GPIO_Configuration(void)

{

GPIO_InitTypeDef GPIO_InitStructure; GPIO設定用構造体

↓以下のコメントアウトされている部分は、TIM3_CHxをAlternate Functionのdefaultアサインする手続きです.つまり22,23,26,27pinにTIM3_CHxがアサイン.

/*

// TIM3_CH12 output ports are PA6,PA7 (default)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA, &GPIO_InitStructure);

// TIM3_CH34 output ports are PB0,PB1 (default)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;

GPIO_Init(GPIOB, &GPIO_InitStructure);

*/

GPIO_Pinの選択肢は、GPIO_Pin_0~GPIO_Pin_15およびGPIO_Pin_Allです.

GPIO_Modeの選択肢はつぎです.

GPIO_Mode_AIN アナログ入力

GPIO_Mode_IN_FLOATING floating入力(pull-upもpull-downもしない)

GPIO_Mode_IPD pull-down入力

GPIO_Mode_IPU pull-up入力

GPIO_Mode_Out_OD open-drain出力

GPIO_Mode_Out_PP push-pull出力 (フツーの出力)

GPIO_Mode_AF_OD Altanate Function open-drain出力 (peripheralのIOピンとしてアサイン)

GPIO_Mode_AF_PP Altanate Function push-pull (peripheralのIOピンとしてアサイン)

GPIO_Speedの選択肢は、

GPIO_Speed_2MHz

GPIO_Speed_10MHz

GPIO_Speed_50MHz

GPIO_Init()は2回登場してます.

GPIOA-6(31)とGPIOA-7(32)にAFのdefaultをアサインさせとります.

GPIOB-0(26)とGPIOB-1(27)にAFのdefaultをアサインさせとります.

↓以下の部分は、TIM3_CHxをAlternate Functionのremapアサインする手続きです.つまり37,38,39,40pinにTIM3_CHxがアサイン.

// TIM3_CH1234 output ports are PC6,PC7,PC8,PC9 (Remap)

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOC, &GPIO_InitStructure);

GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);

構造体の設定とGPIO_Init()は既出なので説明は割愛します.

問題はGPIO_PinRemapConfig()です.こやつがremapにしろ!と命じています.

第1引数の選択肢は、たくさんあるので末尾に記します.

}

↓debug用の関数です.とりあえず使ってないので無視しときます.

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t* file, uint32_t line)

{

while (1) {}

}

#endif

GPIO_PinRemapConfig()の第1引数の選択肢リスト.

下記の選択肢を使ったらなにが起きるのかは試してません.

いまは名前から推測するってことでおしまいにつき.

GPIO_Remap_SPI1 : SPI1 Alternate Function mapping

GPIO_Remap_I2C1 : I2C1 Alternate Function mapping

GPIO_Remap_USART1 : USART1 Alternate Function mapping

GPIO_Remap_USART2 : USART2 Alternate Function mapping

GPIO_PartialRemap_USART3 : USART3 Partial Alternate Function mapping

GPIO_FullRemap_USART3 : USART3 Full Alternate Function mapping

GPIO_PartialRemap_TIM1 : TIM1 Partial Alternate Function mapping

GPIO_FullRemap_TIM1 : TIM1 Full Alternate Function mapping

GPIO_PartialRemap1_TIM2 : TIM2 Partial1 Alternate Function mapping

GPIO_PartialRemap2_TIM2 : TIM2 Partial2 Alternate Function mapping

GPIO_FullRemap_TIM2 : TIM2 Full Alternate Function mapping

GPIO_PartialRemap_TIM3 : TIM3 Partial Alternate Function mapping

GPIO_FullRemap_TIM3 : TIM3 Full Alternate Function mapping

GPIO_Remap_TIM4 : TIM4 Alternate Function mapping

GPIO_Remap_PD01 : PD01 Alternate Function mapping

GPIO_Remap_TIM5CH4_LSI : LSI connected to TIM5 Channel4 input capture for calibration

GPIO_Remap_ADC1_ETRGINJ : ADC1 External Trigger Injected Conversion remapping

GPIO_Remap_ADC1_ETRGREG : ADC1 External Trigger Regular Conversion remapping

GPIO_Remap_SWJ_NoJTRST : Full SWJ Enabled (JTAG-DP + SW-DP) but without JTRST

GPIO_Remap_SWJ_JTAGDisable : JTAG-DP Disabled and SW-DP Enabled

GPIO_Remap_SWJ_Disable : Full SWJ Disabled (JTAG-DP + SW-DP)

GPIO_Remap_TIM15 : TIM15 Alternate Function mapping (only for Value line devices)

GPIO_Remap_TIM16 : TIM16 Alternate Function mapping (only for Value line devices)

GPIO_Remap_TIM17 : TIM17 Alternate Function mapping (only for Value line devices)

GPIO_Remap_CEC : CEC Alternate Function mapping (only for Value line devices)

GPIO_Remap_TIM1_DMA : TIM1 DMA requests mapping (only for Value line devices)