ＴＯＰＰＥＲＳ基礎実装セミナー（STM32F401/446 Nucleo版：基本）

基礎編：２日目
TOPPERSプロジェクト

教育ワーキング・グループ

2023/09/16

TOPPERSプロジェクト認定1

本教材の利用条件
NEXCESS基礎コース01
組込みソフトウェア開発技術の基礎
Copyright (C) 2006-2007 by 名古屋大学 組込みソフトウェア技術者人材養成プログラム
Copyright (C) 2006-2007 by本田晋也
上記著作権者は，以下の(1)～(4)の条件を満たす場合に限り，本コンテンツ（本コンテンツを改変・翻訳したものを含む．
以下同じ）を使用・複製・改変・翻訳・再配布（以下，利用と呼ぶ）することを無償で許諾する．

(1) この枠内の著作権表記等が，そのままの形でコンテンツ中に含まれていること．
(2) 本コンテンツを再配布する場合には，再配布の形態等を，以下のウェブサイトから報告すること．
http://www.nces.is.nagoya-u.ac.jp/NEXCESS/REPORT/(3) 本コンテンツを改変・翻訳する場合には，コンテンツを改変・翻訳した旨の記述を，コンテンツ中に含めること．また，改変・翻訳者の著作権表記等は，この枠内の著作権表記等とは別に行うこと．
(4) 本コンテンツの利用により直接的または間接的に生じるいかなる損害からも，上記著作権者を免責すること．
※ 本コンテンツの一部は，文部科学省 科学技術振興調整費により，名古屋大学 組込みソフトウェア技術者人材養成プログラム（NEXCESS）の一環として作成しました．
※ 本コンテンツ中に記載されている商品名やサービス名などは，各社の商標または登録商標です．

2023/09/16

TOPPERSプロジェクト認定2

本ドキュメントに関して
１． 著作権に関しての表記-----------------------------------------------------------------------------------------------------------<TOPPERS基礎実装セミナー(STM32F4-01/446 Nucleo-64版：基本）２日目>
Copyright (C) 2006-2007 by 名古屋大学 組込みソフトウェア技術者人材養成プログラム
Copyright (C) 2006-2007 by 本田晋也 名古屋大学
Copyright (C) 2007-2021 by 竹内良輔 （株）リコー
上記著作権者は、以下の (1)～(3) の条件を満たす場合に限り、本ドキュメント（本ドキュメントを改変したものを含む。
以下同じ）を使用・複製・改変・再配布（以下、利用と呼ぶ）することを無償で許諾する。
(1) 本ドキュメントを利用する場合には、上記の著作権表示、この利用条件および以下の無保証規定が、そのままの形でドキュメント中に含まれていること。
(2) 本ドキュメントを改変する場合には、ドキュメントを改変した旨の記述を、改変後のドキュメント中に含めること。
ただし、改変後のドキュメントがTOPPERSプロジェクト指定の開発成果物である場合には、この限りではない。
(3) 本ドキュメントの利用により直接的または間接的に生じるいかなる損害からも、上記著作権者およびTOPPERSプロ
ジェクトを免責すること。また、本ドキュメントのユーザまたはエンドユーザからのいかなる理由に基づく請求からも、上記著作者およびTOPPERSプロジェクトを免責すること。
本ドキュメントは、無保証で提供されているものである。上記著作権者およびTOPPERSプロジェクトは、本ドキュメントに関して、特定の使用目的に対する適合性も含めて、いかなる保障もしない。また、本ドキュメントの利用により直接的
または間接的に生じたいかなる損害に関しても、その責任を負わない。
------------------------------------------------------------------------------------------------------------

２．

本ドキュメントに関するご意見・ご提言・ご感想・ご質問等がありましたら、TOPPERSプロジェクト事務
局までE-Mailにてご連絡ください。

３．

本ドキュメントの内容は、内容の改善や適正化の目的で予告無く改定することがあります。
本ドキュメントでは、Microsoft社のClip Art Galleryコンテンツを使用しています。
TRONは”The Real-time Operating system Nucleus”の略称です。ITRONは”Industrial TRON”の略称です。
μITRONは”Micro Industrial TRON”の略称です。TOPPERS/JSPはToyohashi Open Platform for Embedded
Real-Time System/Just Standard Profile Kernelの略称です。」

本ドキュメント中の商品名及び商標名は、各社の商標または登録商標です。

2023/09/16

TOPPERSプロジェクト認定3

スケジュール

２日目

1. メモリマップドレジスタの操作方法の確認

0.5時間

2. ポーリングプログラム

3.0時間

3. 割込みプログラム

2.0時間

4. まとめ

0.5時間

2023/09/16

TOPPERSプロジェクト認定4

概要• マイコンボードを用いて組込みプログラムミングの基礎を学ぶ• メモリマップドレジスタの操作方法の確認– デバッガを用いてデバイスレジスタを操作する• ポーリングプログラミング– LED，スイッチ, タイマ, シリアルI/Oを操作するプログラムの作成• 割込みプログラミング– スイッチ，タイマ，シリアルI/Oからの事象を割込みにより扱うプログラムの作成• カップラーメンタイマの作成– スイッチ，タイマによりカップラーメンタイマを実現
2023/09/16

TOPPERSプロジェクト認定

5

メモリマップドレジスタの操作方法の確認
1. ROMモニタの操作
2. ＬＥＤの接続
3. ＬＥＤの操作

2023/09/16

TOPPERSプロジェクト認定

6

メモリマップドレジスタの操作方法の確認 : 目的
LEDが接続されているプログラマブル入出力ポートを例にメモリマップドレジスタの操作方法を学ぶ• ROMモニタを用いると任意のアドレスのメモリの読み書きが可能• Cortex-M4の周辺機能の制御レジスタはメモリ空間に配置されている（メモリマップドレジスタ）– ROMモニタからLEDが接続されているポートを操作可能•

ROMモニタの E(L)コマン
ドによるメモリの読み書き

2023/09/16

TOPPERSプロジェクト認定

7

ROMモニタの操作方法• ST-LINK USBをケーブルでパソコンに接続• デバイスマネージャでCOMポートを確認• TeraTermを立ち上げ• TeraTermを設定を確認して、一度リセットボタンを押す

リセットボタン• コマンドを使ってモニタを操作する– ？ Enterで、使用可能なコマンドの一覧を表示する

2023/09/16

TOPPERSプロジェクト認定

8プロトタイピング・シールド回路図

LED1- PA6
LED2 –PA7

USER SW - PB4

2023/09/16

TOPPERSプロジェクト認定

9

ROMモニタの操作方法 : TeraTermシリアルポート• ROMモニタはUARTを使用して、ボードに対して種々の設定や確認を行うことができます• シリアルポート設定で設定を行います

接続ポート
ボーレート

2023/09/16

TOPPERSプロジェクト認定10

デバッガの操作方法：読み出し• dl コマンドで４バイト単位のメモリDUMPができます• db コマンドは１バイト単位、dw コマンドは２バイト単位

dl <address> enter

2023/09/16

TOPPERSプロジェクト認定11

デバッガの操作方法 : 書き込み••el コマンドにて４バイト単位でのデータ書き込みができます
el アドレスで、データ入力モードに移行し、データを入力すると設定ア
ドレスにデータを書き込みます• ‘.’ enterにて、データ入力モードを終了します
RAM領域の0x20000000番地と0x20000004番地に0x87654321と0x12345678を書き込む

2023/09/16

TOPPERSプロジェクト認定12

周辺デバイス ： プルアップとプルダウン• デジタル回路は電圧の高低（HiとLo）で’0’と’1’を判断する
例）電源電圧が5vの場合，Hiが4～5v, Loは0～1v• LSIへの入力はHiかLoの電圧になるようにしなければならない
プルアップとプルダウン• GPIO入力ポートとしてスイッチ等を接続する場合，入力を電気的に安定させるための回路構成
プルアップの例
スイッチON

2023/09/16

TOPPERSプロジェクト認定

スイッチOFF13

周辺デバイス ：プロセッサとLEDとの接続の関係
プロトタイピング・シールドの例– ポートレジスタの値が1でLEDが点灯する
Cortex-M4

メモリ

CPU
PC
0x20000120

アドレス
0x20000000

メモリ書込み命令

プログラム
movw r3,#3096
movt r3,#16386
strh r0,[r3, #0]0x20000120

GPIOA
ポートレジスタ… 10…

LED2 : Off

0

LED1 : On

1

2023/09/16

TOPPERSプロジェクト認定

140

LEDの接続 ： マニュアルによる確認• LEDはプルアップされている• 直接、マイコンのプログラマブル入出力ポートに接続されている– マイコンから”1”を出力すると点灯する
Arduinoコネクタ

ボード

LED1-PA6に接続 LED2-PA7に接続

2023/09/16

TOPPERSプロジェクト認定15

LEDの接続 ： レジスタ構成• LEDはポートGPIOAに接続：初期化用– モードレジスタ : GPIOA_MODER
0x40020000– スピードレジスタ：GPIOA_OSPEEDR
0x40020008– 出力タイプレジスタ：GPIOA_OTYPER
0x40020004– プルアップダウンレジスタ：GPIOA_PUPUDR 0x4002000C

2023/09/16

TOPPERSプロジェクト認定16

LEDの接続：レジスタ構成• データ設定レジスタ– インプットデータレジスタ– アウトプットデータレジスタ– ビットセットリセットレジスタ

0x40020010
0x40020014
0x40020018• GPIOAに１６ビットの対応ピンがある：BSRRを使用– 1を書き込んだビットをリセット:0、セット:1に変更– ビットセット
0x40020018：16ビット– ビットリセット
0x4002001A:16ビット

2023/09/16

TOPPERSプロジェクト認定17

LEDの接続 ： 設定値
レジスタを操作して全てのLEDを点灯させる• ポートを構成するレジスタとLEDとの接続を整理すると全てのLEDを点灯させるには以下の設定が必要– GPIOAのクロック有効化(0x40023830)の0対応ビットを1– モードレジスタ （0x40020000）の7,6対応ビットに01:General purpose
output modeを設定– 出力タイプレジスタ（0x40020004）の7,6に0:output push pull
– スピードレジスタ(0x40020008)の7,6対応ビットに10:fast speed– プルアップダウン(0x400200C)の7,6対応ビットに01:pull up• レジスタへ書き込む値（?は変更させない）– 0x40023830: 0x00000001(元の値：0x00000001)→変更不要– 0x40020000 : 0x????5???（“???? ???? 0101 ???? ???? ????"）– 0x40020004 : 0x??????0?(“???? ???? ???? ???? 00?? ????”)– 0x40020008 : 0x????A???（“???????? 1010 ???? ???? ????"）– 0x4002000C: 0x????5???(“???? ???? 0101 ???? ???? ????”)
2023/09/16

TOPPERSプロジェクト認定18

LEDの操作 : 任意のLEDの点灯•

次のパターンでLEDを点灯させるためのポートレジス
タへ書き込む値を考え，実際に確認せよ
1. LED2:OFF LED1:OFF
2. LED2:ON LED1:ON
3. LED2:OFF LED1:ON
4. LED2:ON LED1:OFF

2023/09/16

TOPPERSプロジェクト認定19

LEDの操作 : 任意のLEDの点灯
1. LED2:OFF LED1:OFF•RESET(0x4002001a):0x00c0 : “0000 0000 1100 0000"

2. LED2:ON LED1:ON•

SET(0x40020018):0x00c0 : “0000 0000 1100 0000“

3. LED2:OFF LED1:ON••

SET(0x40020018):0x0040 : "0000 0000 0100 0000“
RESET(0x4002001a):0x0080 : “0000 0000 1000 0000”

4. LED2:ON LED1:OFF••

2023/09/16

SET(0x40020018):0x0080 : “0000 0000 1000 0000“
RESET(0x4002001a):0x0040 : “0000 0000 0100 0000”

TOPPERSプロジェクト認定20

ポーリングプログラム
1. ＬＥＤプログラム
・プロジェクトの作成方法
2. スイッチプログラム
3. タイマプログラム
4. シリアルI/Oプログラム

2023/09/16

TOPPERSプロジェクト認定21

ポーリングプログラミング : 目的
周辺デバイスを操作するプログラムの書き方を学ぶ• 初期設定，データの入出力，事象の待ち方– スイッチのONやタイマのタイムアウトなどの外部事象
はポーリングによって扱う• プログラミング対象の周辺デバイス– LED（プロマブル入出力ポート出力）– スイッチ（プロマブル入出力ポート入力）– タイマ– シリアルI/O
2023/09/16

TOPPERSプロジェクト認定22

ポーリングプログラミング : プログラムファイル• プログラムファイルの置き場所– 教材ディレクトリ/base1/program– MSYS2のホームディレクトリにtoppersを作り、base1を
toppersの下にコピーしてください• プログラム一覧– led_shift: LEDシフト点灯プログラム– led_count: LEDカウント点灯プログラム– switch_push : PUSHスイッチプログラム– timer: タイマプログラム– uart: シリアルI/Oプログラム
led_shift以外は、回答となるため、プログラム
は用意しない2023/09/16

TOPPERSプロジェクト認定23

LEDプログラム : led_shift 概要• 次のパターンでLEDを点灯させる– LED全てOFF → LED1 ON → LED2 ON → LED1
ON → LED2 ON → LED全てOFF
LED1

LED2

0x0

0x1

0x2

0x2

0x1

• 学習内容– プログラムの全体像の理解– ビット操作（セット，クリア）– デバイスレジスタ操作（様々な記法）
ポートからの出力– スタートアップルーチン，セクション• 構成ファイル
– led_shift.c/startup_stm32f4xx.S
2023/09/16

TOPPERSプロジェクト認定24

led_shift : メイン関数– 後述するスタートアップルーチンから呼び出される
LED接続ポート
の初期化
ウェイト

処理内容は
同一であるが記法が異なる2023/09/16

void
main(void){
led_init();for (;;) {led_out_no_macro(0x00);
busy_wait();
led_out_macro1(LED1);
busy_wait();
led_out_macro2(LED2);
busy_wait();
led_out_struct(LED1);
busy_wait();
led_out_sil(LED2);
busy_wait();}}
TOPPERSプロジェクト認定

LED全消灯LED1点灯LED2点灯LED1点灯LED2点灯25

led_shift : ポートレジスタへの書き込み• LED点灯・消灯制御– GPIOAのBSRRレジスタ（0x40020018,0x4002001A）への書き込み– 特定アドレスの読み書きが必要• アセンブリコード
movw r2, #26 /* 0x01a */movt r2, #16386 /* 0x4002 */mov.w r1, #192 /* 0x00c0 */r1, [r2, #0]
書き込み : strh
• C言語
– ポインタを使用– アドレスをポインタ変数にキャスト書き込み : *((unsigned short *)(0x4002001a)) = 0x00c0;
読み込み : tmp = *((unsigned long *)(0x40020014));2023/09/16

TOPPERSプロジェクト認定26

led_shift : volatile修飾子• volatile– ポインタを用いてレジスタをアクセスする場合は，最適化を抑制するため，常にvolatile指定をつける必要ある

書き込み : *((volatile unsigned short *)(0x4002001a)) = 0x00c0;
読み込み : tmp = *((volatile unsigned long *)(0x40020014));•volatile修飾子の場所– 以下の記述でもコンパイルは通るが，最適化は抑制できないので注意が必要である（ポインタ変数がvolatile）– ポインタ変数が指し示す先をvolatileにしなければならない
tmp = *((unsigned long * volatile )(0x40020014));2023/09/16

TOPPERSプロジェクト認定27

led_shift : ポートレジスタ書き込み関数• LED点灯・消灯制御– GPIOAのBSRRレジスタ（0x40020018）への書き込み– アクセスサイズが2byte×2なので，unsigned short 型のポインタ
へキャストして書き込む
void led_out(unsigned short led_data){
unsigned short reg1 = ~led_data;
unsigned short reg2 = led_data;*((volatile unsigned short *) 0x4002001A) = reg1; /* 書き込み*/*((volatile unsigned short *) 0x40020018) = reg2;}

今回はこれで十分だが、再利用性を考えると不十分なコード?
• 将来的にGPIOAのLEDが接続されているビット以外のビットに他の回路が接続された場合、このコードの実行により発生する不具合は?
2023/09/16

TOPPERSプロジェクト認定28

led_shift : 変更値以外の保存• 再利用性を考えると操作対象のビット以外は変更するべきではない– 操作対象のビット以外は変更前の値を書き込む• 変更前の値をどこから読むか?
– ポートレジスタから?
通常のMemory Mapped IOの場合、ポートレジスタに直接データを書き込む、
この場合、周辺回路のレジスタはメモリマッピングされているが、メモリとは異なり書き込んだ値が読み込めるとは限らないため注意が必要

STM32F4xxのポートレジスタの仕様（users manual chapter 10から引用）– 設定を直接参照する場合は、GPIO port output data registerを使用します– このレジスタは、設定値の保存や読み出しができます• ポートレジスタが値を保持しない場合はグローバル変数に値を保持する

2023/09/16

TOPPERSプロジェクト認定29

led_shift : 特定ビットのみの変更
•
•
•

•

•

変更前の値はポートレジスタから取得可能
取得した値のうち、LEDが接続されているビット7-6にのみ
引数 led_data の値を反映させる
例えば..
– ポートレジスタの値(LED1:ON): 0x4160 (“0100 0001 0110 0000")– 引数led_data(LED2:ON): 0x0080 (“0000 0000 1000 0000")– 書き込みたい値は: 0x41A0 (“0100 0001 1010 0000")
プログラムによる0x41A0の生成手順
1. ポートレジスタの値のビット7,6をクリア（0x4120 （“0100 0001 0010 0000”））
2. led_dataのビット7,6のみを取り出す
3. 1.と2.で生成した値の論理和を生成（“0100 0001 0010 0000”|“0000 0000 1000 0000”= “0100 0001 1010 0000”）
特定ビットのクリアや特定ビットの取り出しが必要
ビット演算により実現
2023/09/16

TOPPERSプロジェクト認定30

led_shift : led_outの変更• ポートレジスタのビット7,6以外は元の値を保持
STM32F4xxの場合、SET、RESETが別ポートであり,0を書き込んだビットは変化しないため、ビット演算の必要はない
1. オフ（0）にするビットを取り出す
void
led_out_no_macro(unsigned short led_data){
unsigned short reg1, reg2;
2. オン（1）にするビットを取り出す
reg1 = ~led_data & 0x00c0;
reg2 = led_data & 0x00c0;
3. BSSRにオンにするデータを書き込む*((volatile unsigned short *)0x4002001A) = reg1;*((volatile unsigned short *)0x40020018)) = reg2;}
4. BSRRにオフにするデータを書き込む
2023/09/16

TOPPERSプロジェクト認定31

led_shift : マクロによる記述1
定数はマクロ（記号定数）で記述する• レジスタのアドレスや接続ビットの値を直接プログラム中に記述すると可読性が悪くなる（マジックナンバー）• レジスタ値や接続ビットの変更に対応が可能• GPIOAレジスタのマクロ定義#define TADR_GPIOA_CLR#define TADR_GPIOA_SET

0x4002001A
0x40020018• GPIOAのLEDの接続ビット定義#define PINPOSITION6#define PINPOSITION7#define#define#define2023/09/16

6
7

LED1
(1<<PINPOSITION6)
LED2(1<<PINPOSITION7)
LED_MASK (LED1 | LED2)
TOPPERSプロジェクト認定/* LED1ビット定義 *//* LED2ビット定義 */32

led_shift : マクロによるled_outの書き換え1• 何をしているプログラムなのか分かりやすくなるvoid
led_out_macro1(unsigned short led_data){
unsigned short reg1, reg2;
reg1 = ~led_data & LED_MASK;
reg2 = led_reg & LED_MASK;*((volatile unsigned short *)TADR_GPIOA_CLR) = reg1;*((volatile unsigned short *)TADR_GPIOA_SET) = reg2;}

2023/09/16

TOPPERSプロジェクト認定33

led_shift : マクロによる記述2
デバイスレジスタでは，ポインタへのキャストの部分も含めてマクロ化する場合が多い• 型の指定間違いを防ぐ– デバイスレジスタにはアクセスサイズがある• 記述が簡素になる• マクロ化の方法や命名規則はプログラムやプロジェクト
単位で一定化しておく• GPIOAレジスタ#define TREG_GPIOA_CLR ((volatile unsigned short*)TADR_GPIOA_CLR)#define TREG_GPIOA_SET ((volatile unsigned short*)TADR_GPIOA_SET)• ポインタ部までマクロ化(プログラム例はない)#define TREG_GPIOA_CLR *((volatile unsigned short*)TADR_GPIOA_CLR)#define TREG_GPIOA_SET *((volatile unsigned short*)TADR_GPIOA_SET)
2023/09/16

TOPPERSプロジェクト認定34

led_shift : マクロによるled_outの書き換え2
void
led_out_macro2(unsigned short led_data){
unsigned short reg1, reg2;
reg1 = ~led_data & LED_MASK;
reg2 = led_data & LED_MASK;*TREG_GPIOA_CLR = reg1;*TREG_GPIOA_SET = reg2;}•

1行での記述も可能*TREG_GPIOA_CLR = ~led_data & LED_MASK;

2023/09/16

TOPPERSプロジェクト認定35

led_shift : 構造体による記述
構造体を用いてデバイスレジスタのアドレスを指定する• ビット単位の読み書きが容易に記述可能– コンパイラ依存であり，利用できないコンパイラもある• 記述方法もコンパイラによって異なる/* ビット構造体 */ビット
struct bit_def {フィールド
char b0:1;
char b1:1;
char b2:1;
char b3:1;
char b4:1;
char b5:1;
char b6:1;
char b7:1;};
2023/09/16

struct byte_def{
struct bit_def bit0;
struct bit_def bit1;};/* ワード構造体 */union half_def{
struct byte_def byte;
unsigned short half;};

TOPPERSプロジェクト認定36

led_shift : 構造体による書き換え
バイトアクセスvoid
による記述
led_out_struct(unsigned short led_data){
union half_def reg1, reg2;

reg1.half = 0;
reg2.half = 0;
reg1.byte.bit0.b7 = ((LED2 & led_data) == 0)? 1 : 0;
reg2.byte.bit0.b7 = ((LED2 & led_data) != 0)? 1 : 0;
reg1.byte.bit0.b6 = ((LED1 & led_data) == 0)? 1 : 0;
reg2.byte.bit0.b6 = ((LED1 & led_data) != 0)? 1 : 0;
*TREG_GPIOA_CLR = reg1.half;*TREG_GPIOA_SET = reg2.half;

ビットアクセス
による記述}

2023/09/16

TOPPERSプロジェクト認定37

led_shift : デバイスアクセス関数による記述
デバイスへのアクセスを専用の関数により実現• プログラムのハードウェア依存性が弱まる• シミュレーション環境への対応が容易
例）デバイスドライバ設計ガイドラインのアクセスインタフェース– ハーフデータの読み込みと書き込み

unsigned short
sil_reh_mem(unsigned int addr){
return *((volatile unsigned short *)addr);}void
sil_wrh_mem(unsigned int addr, unsigned short bdata){*((volatile unsigned short *)addr) = bdata;}
2023/09/16

TOPPERSプロジェクト認定38

led_shift : デバイスアクセス関数による書き換え• アクセスサイズによって呼び出す関数が異なる

void
led_out_sil(unsigned short led_data){
unsigned short reg1, reg2;
reg1 = ~led_data & LED_MASK;
reg2 = led_data & LED_MASK;
sil_wrh_mem(TADR_GPIOA_CLR, reg1);
sil_wrh_mem(TADR_GPIOA_SET, reg2);}

2023/09/16

TOPPERSプロジェクト認定39

led_shift : 初期化関数 led_init()• ポートを出力方向に設定し，全LEDを消灯• LED接続ビット以外を変更しないようにする
GPIOAのクロック
void
led_init(void){
スタート
unsigned long reg;
LEDの位置でunsigned char pinpos;
LOOP*TREG_RCC_AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
for(pinpos = PINPOSITION6 ; pinpos <= PINPOSITION7 ; pinpos++){reg = *TREG_GPIOA_MODER & ~(3 << (pinpos *2));*TREG_GPIOA_MODER = reg | (GPIO_Mode_OUT << (pinpos * 2));
reg = *TREG_GPIOA_OSPEEDR & ~(3 << (pinpos * 2));
*TREG_GPIOA_OSPEEDR = reg | (GPIO_Speed_50MHz << (pinpos *2));*TREG_GPIOA_OTYPER |= (GPIO_Otype_PP << pinpos);
reg = *TREG_GPIOA_PUPDR & ~(3 << (pinpos * 2));
*TREG_GPIOA_PUPDR = reg | (GPIO_PuPd_UP << (pinpos * 2));
*TREG_GPIOA_CLR = 1 << pinpos;}
2023/09/16

TOPPERSプロジェクト認定40

led_shift : ビジーウェイト関数 busy_wait()• forループにより任意時間待ちを生成• ループ回数は1msをベース• ループ変数はlong型– 1msをベースに３２ビット長の時間に対応するため– 引数１に対して、1ナノ秒のソフト待ちを行うsil_dly_nse
を用意#define DELAY_LOOP (250*1000)
void
busy_wait(void){
volatile long i;
for(i = 0; i < DELAY_LOOP; i++)sil_dly_nse(1000);}
2023/09/16

TOPPERSプロジェクト認定

Long(32bit)
指定

41

led_shift : startup_stm32f4xx.S
アセンブリ言語で記述されたファイル• 各プログラムで共通に使用できる• セクションの配置– ROM実行の場合、ROM上に配置– RAM実行の場合、RAM上に配置• ベクタテーブル– ROM実行の場合、ROM上に配置– RAM実行の場合、RAM上に配置– ベクタテーブルの設定アドレスを切り替え• スタートアップルーチン– セクションの初期化– _main関数の呼び出し
2023/09/16

TOPPERSプロジェクト認定42

led_shift : startup_stm32f4xx.S （ROM実行）• リンクファイル：stm32f4xx_rom.ldで指定; ------- RAM 領域 ------------ORG
0x20000000
SECTION .data
SECTION .bss

; ------- ROM 領域 ------------ORG
0x08000000
SECTION .vector
SECTION .text
SECTION .data

セクション• .vector:割込みベクタ、書き換え不可• .text:プログラム、書き換え不可データ• .data: 初期値ありデータROM化される場合、ROM上の初期値LOADADDR(.data)
RAM上の参照領域ADDR(.data)に分けられる
スタートアップルーチンでROM上のデータをRAM上にコピーする• .bss: 初期値なしデータ
スタートアップルーチンでゼロに初期化
2023/09/16

TOPPERSプロジェクト認定43

led_shift : startup_stm32f4xx.S （RAM実行）• リンクファイルstm32f4xx_ram.ldで指定; ------- RAM 領域 ------------ORG
0x20000000
SECTION .vector
SECTION .text
SECTION .data
SECTION .bss

セクション
• すべてのデータはROMモニタのldコマンドでダウンロード• .vector:割込みベクタ、書き換え不可• .text:プログラム、書き換え不可データ• .data: 初期値ありデータ
RAM上にダウンロードするため、ROM領域からの.dataのコピーは不要• .bss: 初期値なしデータ
ROMモニタのgoコマンド実行後、ゼロに初期化
2023/09/16

TOPPERSプロジェクト認定44

led_shift : startup_stm32f4xx.S 固定ベクタ• Cortex-M4はリセット後，0x08000004番地のベクタから実行を始める• ROM実行の場合、0x08000004にリセット関数(Reset_Handler)へのポインタを記載する• 0x0800000番地にリセット時のmspのアドレスを置く
g_pfnVectors:.word _estack.word Reset_Handler.word NMI_Handler.word HardFault_Handler.word MemManage_Handler.word BusFault_Handler.word UsageFault_Handler.word 0
.word 0
.word 0
.word 0
.word SVC_Handler.word DebugMon_Handler

2023/09/16

RESET時のス
タックポインタ
リセット時の関
数ポインタ

TOPPERSプロジェクト認定45

startup_stm32f4xx.S スタートアップルーチン• アセンブリ言語で記述– 設定しているレジスタをマニュアルでチェック– C言語と同様に定数はマクロ化
Reset_Handler:/* Copy the data segment initializers from flash to SRAM */movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4_sdataから_edataまでに_sidataをコピー

LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
2023/09/16

TOPPERSプロジェクト認定46

startup_stm32f4xx.S セクションの初期化
ldr r2, =_sbss
b LoopFillZerobss/* Zero fill the bss segment. */FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss_sbssから_ebss
までをゼロクリア

hardware_init_hookがリンクされていれば実行/* Call the clock system intitialization function.*/ldr r0, =hardware_init_hook
cmp r0, #0x00
beq start_0
blx r02023/09/16

TOPPERSプロジェクト認定47

led_shift : start.S main関数の呼び出し• ハードウェアの初期化，data,bssの初期化が終了後，_main関数を呼び出す/* Call the application's entry point.*/
start_0:
bl _mainbx lr.size Reset_Handler, .-Reset_Handler• main()という名前で関数を作成するとCのランタイムの初期化処理を自動的に呼び出すルーチンを関数内に入れるコンパイラもあるので注意• GCCでは、それをさけるために、mainを_mainとしている

2023/09/16

TOPPERSプロジェクト認定48

ポーリングプログラム
1. ＬＥＤプログラム
・プロジェクトの作成方法
2. スイッチプログラム
3. タイマプログラム
4. シリアルI/Oプログラム
2023/09/16

TOPPERSプロジェクト認定49

プロジェクトの作成方法
新規プロジェクトの作成方法を学習• プロジェクトのためのディレクトリ
（my_program/led_count）を作成• 作成場所に制約はない• 教材ディレクトリ /programの直下 にある以下のファイルを
my_program フォルダにコピーする
cmsis_f4.hsil.h
startup_stm32f4xx.S
stm32f4_ram.ld
stm32f4_rom.ld
stm32f4xx.c
stm32f4xx.h sys_defs.h2023/09/16

TOPPERSプロジェクト認定50

プロジェクトの作成方法 ： C言語プログラム• コンパイルするC言語プログラムを用意する• program/led_shift/led_shift.cの内容をコピーして，led_countのディレクトリに led_count.c として作成する• Makefileをそのままコピーする

2023/09/16

TOPPERSプロジェクト認定51

Makefileの書き換え• コピーしたMakefileをエデッタで開く• １２行目のOBJNAMEをled_shiftからled_countに変更する

led_shiftからled_countに変更## オブジェクトファイル名の定義#
OBJNAME = led_count#ifdef OBJEXTOBJFILE = $(OBJNAME).$(OBJEXT)#else
OBJFILE = $(OBJNAME)

2023/09/16

TOPPERSプロジェクト認定52

LEDプログラム : led_count 概要•

LEDを2進数の表示器とみなし，一定時間毎に0x00から0x0fまで表示する（カウントアップ）

LED1

LED2

0x0

0x2

0x1

0x3

0x4

…

0xf
• 学習内容– プロジェクトの作成方法– 出力値のエンコード方法• 作成方法– led_shiftをベースに作成
2023/09/16

TOPPERSプロジェクト認定53

led_count : メイン関数• 無限ループで一定時間毎に変数を引数にLEDの表示を更新する関数を呼び出し，変数をインクリメントする• led_out_bdigit()は引数の下位2bitをLEDに反映させる
void_main(void){
unsigned char count = 0;
ハードウェア初期化
led_init();
LED設定
for (;;) {led_out_bdigit(count++);busy_wait();}}2023/09/16

TOPPERSプロジェクト認定54

led_count : 作成

プログラム led_count を作成せよ• 手順– led_out_bdigit()を作成• 引数の下位2bitをそのままポートに書き込むと正しく表示されないため（LED1はビット6に接続），引数見て，対応するビットをONにする必要がある• LED接続ポートへの書き込みは，led_out_xxx() 関数を用いる

2023/09/16

TOPPERSプロジェクト認定55

led_count : led_out_bdigit()• 引数の各ビットをチェックし，0ビット目が’1’ならLED1をON，1ビット
目が’1’ならLED2をONとなるようにled_dataを設定する• led_dataの初期値は，全てのLED OFF とする0x00にしておく
void
led_out_bdigit(unsigned char data){
unsigned short led_data = 0x00;
if ((data & 0x01) == 0x01) {led_data = led_data | LED1;}
if ((data & 0x02) == 0x02) {led_data = led_data | LED2;}...省略...
led_out(led_data);}
2023/09/16

TOPPERSプロジェクト認定

初期値

LED1設定LED2設定LED設定
led_out以下の５つの関数から選ぶ①led_out_no_macro②led_out_macro1③led_out_macro2④led_out_struct⑤led_out_sil

56デバッグ表示の記載例• 正しくLEDが表示されない場合• led_out_bdigitの引数が正しくLEDの設定値になっているか確認のためにsys_printf/sys_puthex文を入れてデバッ
グする（make DEBUG=1でビルド）
void
led_out_bdigit(unsigned char data){...省略...
led_out(led_data);
sys_printf(“led_out_bdight data[%02x]¥n”, led_data);}

2023/09/16

TOPPERSプロジェクト認定57

ポーリングプログラム
1. ＬＥＤプログラム
・プロジェクトの作成方法
2. スイッチプログラム
3. タイマプログラム
4. シリアルI/Oプログラム
2023/09/16

TOPPERSプロジェクト認定58

スイッチプログラム : switch_push 概要• SW1の押下回数を、LEDにバイナリ表示する
LED1

LED2

0x0

0x2

0x1
一回

二回

0x3
三回

0x4
四回

…

0xf
十五回

• 学習内容– ポートからの読み込み• 作成方法– led_countをmy_programにコピーし、ディレクトリ名を書き換えて作成（次のスライドで説明）
2023/09/16

TOPPERSプロジェクト認定59

led_countデレクトリのコピー• 前の演習で作成したmy_program中のled_countディレクト
リのプログラムを元に以下の演習を行います– switch_push(この演習)– timer(ポーリングタイマ)– uart(ポーリングUART)• lcd_countをmy_program中に３つコピーしてそれぞれのディレクトリ名を、上記の３つに変更します

led_countディレクトリを３つコピーして作り、名称をswitch_push/
timer/uartとする

2023/09/16

TOPPERSプロジェクト認定60

switch_push : 作成
プログラム switch_push を作成• 手順– マニュアルによるプッシュスイッチの接続の確認– マクロの定義– 初期化関数 switch_push_init() の作成• ポートを入力モードへ– プッシュスイッチ状態取得関数 switch_push_sense()の作成• ポートの読込み– メイン関数• スイッチの変化を捉える（OFFからONへ)

2023/09/16

TOPPERSプロジェクト認定61

switch_push : プッシュスイッチの接続• マニュアルにより確認する• マイコンのGPIOB:0/INT4ポートに接続されている• ONで電流が流れ、GPIOB:0にSW1の状態が取り込める– マイコンからはONで’0’が，OFFで’1’が読み込める
arduinoコネクタ

ボード

SW1はPB4ポートに接続している

2023/09/16

TOPPERSプロジェクト認定62

switch_push : プッシュスイッチの接続ポート• ポートGPIOBに接続– SW1: ビット4 : PB4• ポートGPIOBの構成レジスタ– モードレジスタ: 0x40020400– プルアップダウンレジスタ : 0x4002040C– ポート読み込みレジスタ : 0x40020410• 初期化– ポート方向レジスタのビット0を入力モードへ（初期値）• スイッチ状態の取得– ポートB4レジスタを読み込む

2023/09/16

TOPPERSプロジェクト認定63

switch_push : マクロ定義• ポインタ型へのキャストまで含めてマクロ化する（方針）• GPIOB関連のレジスタのマクロ定義（定義は自動でインクルード）#define TADR_RCC_APB2ENR 0x40023844#define TADR_GPIOB_MODER 0x40020400#define TADR_GPIOB_PUPDR 0x4002040C#define TADR_GPIOB_IDR
0x40020410#define TREG_RCC_APB2ENR ((volatile unsigned long *)(TADR_RCC_APB2ENR))#define TREG_GPIOB_MODER ((volatile unsigned long *)(TADR_GPIOB_MODER))#define TREG_GPIOB_PUPDR ((volatile unsigned long *)(TADR_GPIOB_PUPDR))#define TREG_GPIOB_IDR((volatile unsigned long *)(TADR_GPIOB_IDR))• GPIOBポートのプッシュスイッチの接続ビット定義#define PINPOSITION4 4#define PSW1
0x0010#define PSW_MASK (PSW1)#define PSW1_PINNO PINPOSITION4
2023/09/16

TOPPERSプロジェクト認定64

switch_push : 初期化関数• GPIOBポートのビット０を入力モードへ– GPIOBポート方向レジスタのビット4を’00’に設定する
void
switch_push_init(void){
ビット0のみ’00’に
unsigned long reg;
設定
unsigned int pinpos = PSW1_PINNO;*TREG_RCC_AHB1ENR |= RCC_AHB1ENR_GPIOBEN;*TREG_RCC_APB2ENR |= RCC_APB2ENR_SYSCFGEN;
reg = *TREG_GPIOB_MODER & ~(3 << (pinpos*2));*TREG_GPIOB_MODER = reg | (GPIO_Mode_IN << (pinpos *2));
reg = *TREG_GPIOB_OSPEEDR & ~(3 << (pinpos * 2));
*TREG_GPIOB_OSPEEDR = reg | (GPIO_Speed_2MHz << (pinpos * 2));
reg = *TREG_GPIOB_PUPDR & ~(3 << (pinpos*2));*TREG_GPIOB_PUPDR = reg | (GPIO_PuPd_NOPULL << (pinpos *2));}

2023/09/16

TOPPERSプロジェクト認定65

switch_push : プッシュスイッチ状態取得関数• switch_push_sense()• GPIOBのビット4の値を取得

unsigned longswitch_push_sense(void){
unsigned long reg;

ビット4のみ有効に

reg = *TREG_GPIOB_IDR ^ PSW1;
return reg & PSW_MASK;}

2023/09/16

TOPPERSプロジェクト認定66

switch_push : main()関数• カウント用変数led_dataの用意• ハードウェアの初期化• 無限ループ内でプッシュスイッチの状態をチェックしてSW1がOFFからONに変化した場合は，led_dataをインクリメントする• 状態の変化の捉え方– プッシュスイッチの以前の状態を保存して比較– ローカル変数を用意して前回の状態取得時の状態を保存する• スイッチの状態（ON,OFF）はマクロ化する#define SW_ON 1#define SW_OFF 02023/09/16

TOPPERSプロジェクト認定67

switch_push : main()関数
void _main(void){
unsigned char led_data = 0; SW1状態保存用変数
unsigned long sw_data;
unsigned char sw1_state = SW_OFF;
led_init();
プッシュスイッチ状態取得
switch_push_init();for (;;) {SW1がOFFからONになったか
sw_data = switch_push_sense();if(((sw_data & PSW1) == PSW1) && (sw1_state == SW_OFF)){
led_data++;
SW1の状態を保存}
sw1_state = ((sw_data & PSW1) == PSW1)? SW_ON : SW_OFF;
led_out_bdigit(led_data);
}
LED設定
}
2023/09/16

TOPPERSプロジェクト認定68ポーリングプログラム
1. ＬＥＤプログラム
・プロジェクトの作成方法
2. スイッチプログラム
3. タイマプログラム
4. シリアルI/Oプログラム
2023/09/16

TOPPERSプロジェクト認定69

タイマプログラム : timer 概要• ハードウェアタイマにより正確に1秒間隔でLEDの点灯をカウントアップする• 学習内容– ハードウェアタイマの使い方– カウントソース，カウント値の決定方法• 作成方法– led_count をコピーし、ディレクトリ名をtimerに書き換えて作成

2023/09/16

TOPPERSプロジェクト認定70

timer : ハードウェアタイマによる時間生成• led_countでは，時間生成にbusy_wait()関数を使用– for()ループにより一定時間を生成• ループでは正確な時間が生成できない– そもそもループでは正確な時間は作れない– 正確な周期処理を実現が困難– 周期 = 周期処理の実行時間 + ループの実行時間• 周期処理の実行時間が変わると，それに合わせてループの長さを変更する必要がある
10ms

2ms

10ms

8ms

4ms

10ms

8ms
12ms

処理時間が増加2023/09/16

TOPPERSプロジェクト認定

周期ミス
71

timer : ハードウェアタイマの概要• stm32F401のハードウェアタイマ
– ３２ビットタイマを７個– タイマモード，４つのマッチレジスタ、カウント方法やク
ロックソースはコントロールレジスタで設定可能• タイマ4を使用– タイマモードを使用• 内部カウントソースによりカウントアップ• マッチカウンタと比較• マッチ後、カウンタリセット– メインクロック84MHzをタイマクロックとして使用– 周辺クロック選択レジスタで変更可能

2023/09/16

TOPPERSプロジェクト認定72

timer : レジスタ• タイムアウトはステータスレジスタのUIFビットにより通知– タイマ側のレジスタのビットがセットされる場合が多い
名前

アドレス

説明

TIM4_CR1

0x40000800

コントロール・レジ
スタ１

TIM4_SR

0x40000810

ステータス・レジス
タ

TIM4_EGR

0x40000814

タイマ0カウンター

TIM4_PSC

0x40000828

プリスケールレジス
タ

TIM4_ARR

0x4000082C

オートリロード・レジ
スタ

RCC_APB1
ENR

0x40023840

APB1クロック・イ
ネーブル・レジスタ

RCC_AHB1
ENR

0x40023830

AHB1クロック・イ
ネーブル・レジスタ

2023/09/16

TOPPERSプロジェクト認定73

timer : タイマ操作の流れ• タイマ４モードを設定– クロックを有効化– 停止とリセット、分周比• リロードカウンタ、プリスケール値を設定– カウントソースと計時したい時間から求める• リロードモード設定• ステータスレジスタをクリアし、タイマ４を起動し、割込み制御レジスタをチェックして，タイムアウトの通知を待つ

2023/09/16

TOPPERSプロジェクト認定74

timer : 作成
プログラム timer を作成• 手順– カウントソースの決定• デフォルト1/1分周を使用– カウント値の決定• リロードカウンタを4000として1秒のタイムアウトを作成– プログラミング• マクロの定義• 初期化関数• 計時関数• メイン関数2023/09/16

TOPPERSプロジェクト認定75

timer : カウントソースの選択
カウントソースを高速にすると精度は高くなるが測定可能な最大時間が小さくなり、カウントソースを低速にするとその逆となる• カウント値とカウントソースの決定– クロックがリロードカウンタ分、発生するとカウントアップする– カウンタ値がプリスケールにマッチするとタイムアウト– カウントソース：システムクロック/1• 1/1(00),1/2(01),1/4(10)から設定可能• STM32F401はシステムクロックが84MHzなので，カウントソースは84MHz– 1/1: 84MHz/1 = 84MHz– 1/2: 84MHz/2 = 42MHz– 1/4: 84MHz/4 = 10.5MHz• STM32F446は180MHzなので、180MHz– カウンタ値が16ビットに収まるようリロードカウンタを4000に設定• 84MHzで１秒を計測するには84000000/65536≒1281• 180MHzで１秒を計測するには90000000/65536≒2747• リロードカウンタで4000にすれば16ビットに収まる
2023/09/16

TOPPERSプロジェクト認定76

timer : カウント値の決定• 分周を1/1、リロードカウンタ4000（1秒）– プリスケール設定値：84,000,000/4000 – 1 = 20999– (STM32F446では180,000,000/4000-1=44999)– リロードカウンタ設定値：4000• その他のレジスタの設定値– TM4_CR1の0ビット目をセットするとカウントをスタート– タイムアウトするとTIM4_SRのビット0(0x01)が‘1’にセットされる– TIM4_SRのビット0から‘0’を読み込んだ後は‘0’にクリアする– (0x0000を書き込んでクリア)
名前

アドレス

設定値

説明

TM4_ARR

0x4000082C

0x0F9F

リロードカウンタ設定

TM4_PSC

0x40000828

0x5207

プリスケール設定

TM4_CR1

0x40000800

0x0089

タイマスタート(0x0088で停止)

2023/09/16

TOPPERSプロジェクト認定77

timer : マクロの定義• タイマ関連のレジスタ(サイズは４バイト：自動でインクルード)/* TIMER用 RCCレジスタ */#define TREG_RCC_APB1ENR ((volatile unsigned long *)0x40023840)/* タイマ4コントロール１レジスタ */#define TREG_TIM4_CR1 ((volatile unsigned short *)0x40000800)/* タイマ4ステータスレジスタ */#define TREG_TIM4_SR ((volatile unsigned short *)0x40000810)/* タイマ4イベントジェネレーションレジスタ */#define TREG_TIM4_EGR ((volatile unsigned short *)0x40000814)/* タイマ4プリスケールレジスタ*/#define TREG_TIM4_PSC ((volatile unsigned short *)0x40000828)/* タイマ4オートリロードレジスタ */#define TREG_TIM4_ARR ((volatile unsigned short *)0x4000082C)• タイマ関連の定義#define TIMER_COUNT
4000#define TIM_CR1_CEN
0x0001#define TIM_CR1_OPM
0x0008#define TIM_CR1_DIR
0x0010#define TIM_CR1_CMS
0x0060#define TIM_CR1_ARPE
0x0080#define TIM_CR1_CKD
0x0300/* ステータスレジスタ設定 */#define TIM_SR_UIF
0x00012023/09/16/* プリスケール値 *//* タイマスタート *//* ワンプラスモード *//* ディレクション *//* CMS *//* オートリロードイネーブル *//* CKD *//* アップデート要求 */TOPPERSプロジェクト認定78

timer : 初期化関数• タイマーのモードを設定する– 停止させる必要があるかはタイマによって異なる– STM32F401はマニュアルに特に記載がない
voidtimer4_init(void){
unsigned short PrescalerValue = 0;
クロックイネーブル
unsigned short tmpcr1 = *TREG_TIM4_CR1;/* TIM4クロックを有効 */*TREG_RCC_APB1ENR |= RCC_APB1ENR_TIM4EN;
モード設定/* カウンタモード、クロック分周設定 */*TREG_TIM4_CR1 = tmpcr1 & ~(TIM_CR1_DIR | TIM_CR1_CKD);
PrescalerValue = ((SYSTEMCORECLOCK)/TIMER_COUNT)-1;/* プレスケール値の設定 */*TREG_TIM4_ARR= TIMER_COUNT;*TREG_TIM4_PSC= PrescalerValue;
プリスケール値設定*TREG_TIM4_EGR= 0x0001;}
2023/09/16

TOPPERSプロジェクト認定79

timer : 計時関数
• 引数で指定された回数タイマで計時する– オートリロードのため，スタートさせると後は自動的に計時を繰り
返す
void
タイマ停止

timer4_wait(int cnt){
while(cnt > 0){
モード設定/* タイマ停止 */*TREG_TIM4_CR1 &= ~TIM_CR1_CEN;/* モード再設定 */*TREG_TIM4_CR1 |= TIM_CR1_ARPE | TIM_CR1_OPM;/* タイマーステータスリセット */割込みフラグクリア*TREG_TIM4_SR = 0x0000;
タイマスタート/* カウントスタート */*TREG_TIM4_CR1 |= TIM_CR1_CEN;
タイムアウト待ち
while((*TREG_TIM4_SR & TIM_SR_UIF) == 0){}cnt--;}
タイマ停止*TREG_TIM4_CR1 &= ~TIM_CR1_CEN;}
2023/09/16

TOPPERSプロジェクト認定

80

timer : メイン関数• led_countのメイン関数を変更– busy_wait() を timer4_wait()に変更

タイマの初期化

LEDの出力2023/09/16

LEDの初期化
void
main(void){
unsigned char count = 0;
led_init();timer4_init();for (;;) {led_out_bdigit(count++);timer4_wait(1);}}タイマにより待つ

TOPPERSプロジェクト認定81ポーリングプログラム
1. ＬＥＤプログラム
・プロジェクトの作成方法
2. スイッチプログラム
3. タイマプログラム
4. シリアルI/Oプログラム
2023/09/16

TOPPERSプロジェクト認定82シリアルI/Oプログラム : uart 概要• シリアルI/Oからデータを受信すると，LEDの点灯をカ
ウントアップし，受け取ったデータをシリアルI/Oに送信する（ポーリング）
USART2 TX/RX
仮想COMでUSBに対応• 学習内容– シリアルI/Oの使い方• 作成方法– led_countをコピーし、ディレクトリ名をuartに書き換えて作成• ボードの設定– USART2のTX/RXをST-LINK仮想COMに接続する
2023/09/16

TOPPERSプロジェクト認定

83

uart : シリアルI/Oのフォーマット
• 通信する双方で同じ設定にする必要がある– ボーレート（BPS） : 9600, 14400, 38400, 57600, 115200– データサイズ: 7bit, 8bit
– パリティ
: なし, even, odd– ストップビット: 1bit, 2bit
– フローコントロール : なし, Xon/Xoff, hardware• ターミナルソフトの設定例•データフォーマット
0 （LSB）

（MSB）

1
…

スタート
ビット

データ（7～8ビット）

2023/09/16

パリティ ストップ
ビット ビット(1～2)

TOPPERSプロジェクト認定

84

uart：ピン・クロック設定• UARTとGPIOA(TX/RX)のクロック設定– AHB1：GPIOAクロックイネーブル– APB1： USART2クロックイネーブル• TX/RXピンのGPIOピン設定– TX/RXピン対応のGPIOAのピン設定
名前

アドレス

説明

TREG_RCC_AHB1ENR

0x40023830

AHB1クロック・イネーブル

TREG_RCC_APB1ENR

0x40023840

APB1クロック・イネーブル

TREG_GPIOA_MODER

0x40020000

GPIOAのモードレジスタ

TREG_GPIOA_OTYPER

0x40020004

GPIOAのアウトプットタイプレジスタ

TREG_GPIOA_OSPEEDR

0x40020008

GPIOAのスピードレジスタ

TREG_GPIOA_PUPDR

0x4002000C

GPIOAのプルアップダウンレジスタ

TREG_GPIOA_AFR0

0x40020020

GPIOAアルタネートファンクションレジスタ

2023/09/16

TOPPERSプロジェクト認定85

uart : 送受信フォーマットとレジスタ• フォーマット– ボーレート（BPS） : 115200，データサイズ : 8bit，パリティ: なし，ストッ
プビット: 1bit，フローコントロール : なし

• 使用チャネル
– UART2を使用– UARTモードで使用，カウントソースはPCLK1（82Mhzの1/2分周）

名前

アドレス

TREG_USART2_SR

0x40004400

ステータスレジスタ

TREG_USART2_DR

0x40004404

データレジスタ

説明

TREG_USART2_BRR 0x40004408

ボーレートレジスタ

TREG_USART2_CR1

0x4000440C

コントロールレジスタ１

TREG_USART2_CR2

0x40004410

コントロールレジスタ２

TREG_USART2_CR3

0x40004414

コントローラレジスタ３

2023/09/16

TOPPERSプロジェクト認定86

uart : 作成
プログラム uart を作成• 手順– 転送速度レジスタの設定値の計算• ボーレートが115200bpsになるよう設定– その他のレジスタの設定値の決定– プログラミング• マクロの定義• 初期化関数• 受信関数• 送信関数• メイン関数2023/09/16

TOPPERSプロジェクト認定87

uart：クロック設定、TX/RX設定
クロック設定とTX/RXのピン設定を行う• APB1ペリフェラルのUSART2クロックを有効に• USART2のTX/RXはGPIOAの2ピン、3ピンに対応– GPIOAのクロックを有効に• TX/RX対応のGPIOAの2ピン、3ピンを以下に設定– MODERを10:アルタネートファンクションモード
– OSPEEDRを10:Fast Speed– OTYPERを0:Output push-pull– PUPDRを01:Pull-UP– AFRを0111:AF7

2023/09/16

TOPPERSプロジェクト認定88uart : 転送速度レジスタの設定値の計算• 転送速度は 115200bps• カウントソースはf（42Mhz:84Mhz/2）を使用• BRRにUSARTDIV（小数点２ケタ）を設定– USARTDIV = f / (0.16×bps)• USARTDIV = ((25 * 42MHz) / (4*115200))• DIV_Masntissa = USARTDIV / 100• DIV_Fraction = USARTDIV – (DIV_Masntissa * 100)
• DIV_Fraction = (((DIV_Fraction * 16) + 50) / 100) & 0xf

2023/09/16

TOPPERSプロジェクト認定89

uart : その他のレジスタの設定値
フォーマットに従って設定値を決定する• コントロールレジスタ２：１ストップビット– STOP[13:12]• コントロールレジスタ１：以下の設定– UARTモード転送データ長8ビット:M[12]– TX/RXモード:TE|RE[4:3]– パリティ禁止:PCE|PS[10:9]• コントロールレジスタ３：ハードウェアフロー設定– CTSE:RTSE[9:8]2023/09/16

TOPPERSプロジェクト認定90

uart : マクロ定義1 : UART2関連のレジスタ#define TADR_RCC_AHB1ENR 0x40023830 /* RCC AHB1ENRレジスタ */#define TADR_RCC_APB2ENR 0x40023844 /* RCC APB2ENレジスタ */#define TADR_GPIOA_MODER 0x40020000 /* AポートMODEレジスタ */:/* RCC レジスタ */#define TREG_RCC_AHB1ENR ((volatile unsigned long *) (TADR_RCC_AHB1ENR))#define TREG_RCC_APB1ENR ((volatile unsigned long *) (TADR_RCC_APB2ENR))/* GPIOAバッファレジスタ */#define TREG_GPIOA_MODER ((volatile unsigned long *) (TADR_GPIOA_MODER))#define TREG_RCC_AHB1ENR ((volatile unsigned long *) (TADR_RCC_AHB1ENR))#define TREG_GPIOA_OSPEEDR ((volatile unsigned long *) (TADR_GPIOA_OSPEEDR))#define TREG_GPIOA_OTYPER ((volatile unsigned long *) (TADR_GPIOA_OTYPER))#define TREG_GPIOA_PUPDR ((volatile unsigned long *) (TADR_GPIOA_PUPDR))#define TREG_GPIOA_AFR0 ((volatile unsigned long *) (TADR_GPIOA_AFR0))/* USART2レジスタ */#define TREG_USART2_SR ((volatile unsigned short *) (TADR_USART2_SR))#define TREG_USART2_DR ((volatile unsigned short *) (TADR_USART2_DR))#define TREG_USART2_BRR ((volatile unsigned short *) (TADR_USART2_BRR))#define TREG_USART2_CR1 ((volatile unsigned short *) (TADR_USART2_CR1))#define TREG_USART2_CR2 ((volatile unsigned short *) (TADR_USART2_CR2))#define TREG_USART2_CR3 ((volatile unsigned short *) (TADR_USART2_CR3))

2023/09/16

TOPPERSプロジェクト認定

91

uart : マクロ定義2 : レジスタの設定値• ビット定義はstm32f4xx.hに定義#define TX2_PINPOS
2/* TX-PIN POSITION */#define RX2_PINPOS
3/* RX-PIN POSITION */#define CR1_CLEAR_MASK (USART_CR1_M | USART_CR1_PCE| USART_CR1_PS | USART_CR1_TE | USART_CR1_RE)#define CR3_CLEAR_MASK (USART_CR3_RTSE | USART_CR3_CTSE)#define GPIO_AF_USART2 0x07 /* USART2 Alternate function */• USART初期化定義#define USART_StopBits_1 0x00 /* ストップビット１ */#define USART_WordLength_8b 0x00 /* Data 8bit */#define USART_Parity_No 0x00 /* No Parity */#define USART_Mode_Rx USART_CR1_RE#define USART_Mode_Tx USART_CR1_TE#define USART_HardwareFlowControl_None 0x00
2023/09/16

TOPPERSプロジェクト認定

92

uart：初期化設定関数• RCC設定とTXピン設定
void
uart2_init(void){
unsigned int tmpreg, apbclock, integerdivider, fractionaldivider;/* RCC設定 */*TREG_RCC_APB1ENR |= RCC_APB1ENR_USART2EN;*TREG_RCC_AHB1ENR |= RCC_AHB1ENR_GPIOAEN;/* TX-PIN設定 */*TREG_GPIOA_MODER &= ~(GPIO_MODER_MODER2 <<(TX2_PINPOS*2));*TREG_GPIOA_MODER |= GPIO_Mode_AF<< (TX2_PINPOS*2);*TREG_GPIOA_OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR2<<(TX2_PINPOS*2));*TREG_GPIOA_OSPEEDR |= GPIO_Speed_50MHz<<(TX2_PINPOS*2);*TREG_GPIOA_OTYPER &= ~(GPIO_OTYPER_OT << TX2_PINPOS);*TREG_GPIOA_OTYPER |= GPIO_OType_PP << TX2_PINPOS;*TREG_GPIOA_PUPDR &= ~(GPIO_PUPDR_PUPDR2 << (TX2_PINPOS*2));*TREG_GPIOA_PUPDR |= GPIO_PuPd_UP<< (TX2_PINPOS*2);

2023/09/16

TOPPERSプロジェクト認定

93

uart：初期化設定関数• RXピン設定とアルティネイトファンクション設定/* RX-PIN設定 */*TREG_GPIOA_MODER &= ~(GPIO_MODER_MODER2 <<(RX2_PINPOS*2));*TREG_GPIOA_MODER |= GPIO_Mode_AF<< (RX2_PINPOS*2);*TREG_GPIOA_OSPEEDR &= ~(GPIO_OSPEEDER_OSPEEDR2<<(RX2_PINPOS*2));*TREG_GPIOA_OSPEEDR |= GPIO_Speed_50MHz<<(RX2_PINPOS*2);*TREG_GPIOA_OTYPER &= ~(GPIO_OTYPER_OT << RX2_PINPOS);*TREG_GPIOA_OTYPER |= GPIO_OType_PP << RX2_PINPOS;*TREG_GPIOA_PUPDR &= ~(GPIO_PUPDR_PUPDR2 << (RX2_PINPOS*2));*TREG_GPIOA_PUPDR |= GPIO_PuPd_UP<< (RX2_PINPOS*2);/* UART AF設定 */*TREG_GPIOA_AFR0 &= ~(0xF << ((TX2_PINPOS & 0x07) * 4));
*TREG_GPIOA_AFR0 |= GPIO_AF_USART2 << ((TX2_PINPOS & 0x07) * 4);*TREG_GPIOA_AFR0 &= ~(0xF << ((RX2_PINPOS & 0x07) * 4));
*TREG_GPIOA_AFR0 |= GPIO_AF_USART2 << ((RX2_PINPOS & 0x07) * 4);2023/09/16

TOPPERSプロジェクト認定

94

uart : 初期化関数 uart2_init• 各制御レジスタを初期化し，送受信を許可する*TREG_USART2_CR2 &= ~USART_CR2_STOP;*TREG_USART2_CR2 |= USART_StopBits_1;*TREG_USART2_CR1 &= ~CR1_CLEAR_MASK;*TREG_USART2_CR1 |= USART_WordLength_8b | USART_Parity_No| USART_Mode_Rx | USART_Mode_Tx;*TREG_USART2_CR3 &= ~CR3_CLEAR_MASK;*TREG_USART2_CR3 |= USART_HardwareFlowControl_None; DEFAILT_SPEEDとPCLK1は/* BRR設定 */sys_defs.hで定義
apbclock = PCLK1;
integerdivider = (25 * apbclock) / (4 * DEFAULT_SPEED);
tmpreg = (integerdivider / 100) << 4;fractionaldivider = integerdivider - (100 * (tmpreg >> 4));
tmpreg |= (((fractionaldivider * 16) + 50) / 100) & 0xf;
*TREG_USART2_BRR = tmpreg;*TREG_USART2_CR1 |= USART_CR1_RXNEIE;*TREG_USART2_CR1 |= USART_CR1_UE;}
2023/09/16

TOPPERSプロジェクト認定95

uart : 受信関数
• データを受信すると，受信制御レジスタのRXNEビット（受信完了フラグ）がセットされる• ポーリングでセットされるのを待つ
unsigned charuart2_pol_getc(void){/* 受信バッファにデータが入るのを待つ */while((*TREG_USART2_SR & USART_SR_RXNE) == 0);/* データ受信 */return (unsigned char)(*TREG_USART2_DR);}

2023/09/16

TOPPERSプロジェクト認定

96

uart : 送信関数
• 送信データを書き込める状態であれば，受信制御レジス
タのTC（送信バッファ空フラグ）がセットされる• ポーリングでセットされるのを待つ
void
uart2_pol_putc(unsigned char c){/* 送信バッファが空くのを待つ */while((*TREG_USART2_SR & USART_SR_TC) == 0);/* データ送信 */*TREG_USART2_DR = c;}

2023/09/16

TOPPERSプロジェクト認定

97

uart : メイン関数• 受信関数でデータを受信後，LEDをカウントアップし，送信関数で受信したデータを送信する
void_main(void){
unsigned char count = 0;
unsigned char c;
led_init();uart2_init();

データ受信

for (;;) {c = uart2_pol_getc();
led_out_bdigit(++count);
uart2_pol_putc(c);}LEDカウントアップ
データ送信}
2023/09/16

TOPPERSプロジェクト認定98割込みプログラミング

1. Cortex-Mの割込みアーキテクチャ
2. スイッチ割込みプログラム
3. 割込みタイマ
2023/09/16

TOPPERSプロジェクト認定99

目的
割込みを用いたプログラミングを学ぶ• 割込みによる事象待ちのために必要な知識– 割込みハンドラ– 割込みベクタテーブル– 割込み関連のレジスタ– 割込み禁止・許可– 割込み優先度• プログラム対象の周辺デバイス– プッシュスイッチ
– タイマ– シリアルI/O
2023/09/16

TOPPERSプロジェクト認定100

割込みプログラミング : プログラムファイル• プログラムファイルの置き場所– 教材ディレクトリ/base1/program• プログラム一覧– int_switch_1 : PUSHスイッチ割込みプログラム1(単一割込み)– int_switch_2 : PUSHスイッチ割込みプログラム2(割込み禁止/割込み応答時間)– int_switch_3 : PUSHスイッチ割込みプログラム3(複数割込み/優先度の設定)– int_timer: タイマ割込みプログラム– int_uart: シリアルI/O割込みプログラム
2023/09/16

TOPPERSプロジェクト認定101Cortex-Mの割込みアーキテクチャ
Cortex-Mはネスト型割込みコントローラ（NVIC）を標準搭載••••

NVICは最大240の割込みをサポート
各割込みには最大256レベルの優先度を動的に設定可能
NVICへの完全なアクセスは特権モードから可能
レベル割込みとパルス割込み– レベル割込みは、デバイスにアクセスするISRによってクリアさせるまで、アサートされ続ける– パルス割込みが、エッジモデルの一種で、パルスのサン
プリングは非同期ではなく、Cortex-MのクロックFCLKの立ち上がりでエッジされることを保証する必要がある

2023/09/16

TOPPERSプロジェクト認定

102

NVICレジスタ
NVICレジスタ設定に割込みの有効無効、割込み優先度を設定する•••

NVIC_ISER割込みの有効化
NVIC_ICER割込みの無効化
NVIC_IPR割込み優先度設定

2023/09/16

TOPPERSプロジェクト認定103Cortex-Mの割込みアーキテクチャ• IRQ割込みはstartup_stm32f4xx.Sにベクタテーブルがすでに定義されている.section .isr_vector,"a",%progbits.type g_pfnVectors, %object.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:.word _estack.word Reset_Handler.word NMI_Handler.word HardFault_Handler：/* External Interrupts */.word WWDG_IRQHandler/* Window WatchDog*/.word PVD_IRQHandler/* PVD through EXTI Line detection */.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */.word RTC_WKUP_IRQHandler/* RTC Wakeup through the EXTI line */.word FLASH_IRQHandler/* FLASH*/:.word EXTI4_IRQHandler/* EXTI Line4*/

2023/09/16

TOPPERSプロジェクト認定104Cortex-Mの割込みアーキテクチャ• EXTI4用の割込みハンドラ（EXTI4_IRQHandler）はstartup_stm32f4xx.S内で.weak設定で定義されており、Default_Handlerにリンクしている• この定義は仮定義で、globalにEXTI4_Handler関数をリンクすれば、その関数が正しい関数として扱われる.weak RTC_WKUP_IRQHandler.thumb_set RTC_WKUP_IRQHandler,Default_Handler.weak FLASH_IRQHandler.thumb_set FLASH_IRQHandler,Default_Handler.weak RCC_IRQHandler.thumb_set RCC_IRQHandler,Default_Handler.weak EXTI4_IRQHandler.thumb_set EXTI4_IRQHandler,Default_Handler
2023/09/16

TOPPERSプロジェクト認定105

割込みプログラミング

1. Cortex-Mの割込みアーキテクチャ
2. スイッチ割込みプログラム
3. 割込みタイマ

2023/09/16

TOPPERSプロジェクト認定106

スイッチ割込みプログラム1 : int_switch_1 概要• SW1を押すと発生する割込み（EXTI4）により，LEDをカ
ウントアップする• 学習内容– 割り込みプログラムの全体像の理解– 割込みベクタテーブル– 割込み制御レジスタ– 割込みハンドラ• 動作確認– switch_pushをコピーして、ディレクトリ名をint_switch_1に修正して作成する

2023/09/16

TOPPERSプロジェクト認定107int_switch_1 : 割込みプログラムの全体像• 割込みをプログラムの視点から見ると– メイン関数を実行中に割込みが入ると、一旦メイン関
数中の処理が中断され，割込みに対応した関数（割込みハンドラ）が実行される– 割込みハンドラの実行が終了すると，メイン関数の処理が再開される• プログラムで必要となる処理– 割込み要因ハードウェアの初期化– 割込みハンドラの用意– 割込みベクタテーブルの用意– 割込み関連の初期化
2023/09/16

TOPPERSプロジェクト認定108

int_switch_1 : 割込み要因ハードウェア
プッシュスイッチ（SW1）を割込み要因として用いる• 接続– GPIOB入出力ポートのビット4（PB4）に接続– PB4は外部割込み4（EXTI4）とピンを共有– ボタンを押すとゼロ(LOW)が入力される• 割込み番号、優先度（NVIC_IPRを参照）– 割込み番号EXTI4：１０番に設定済– 割込みハンドラ、レベルを設定• 外部割込みのモード設定：ユーザーマニュアルCapter12.2.1
参照– PINをEXTI4に設定、モードを立上がりエッジに設定する– キーを押し、離した時点で割込みが発生
2023/09/16

TOPPERSプロジェクト認定109int_switch_1 : 割込み要因ハードウェアの初期化
ポート関連のレジスタと割込み制御関連のレジスタの初期化が必要• ポート関連レジスタの初期化– ポーリングプログラミング switch_push の初期化関数を流用• 割込み制御関連のレジスタ– 割込み制御レジスタ• 割込みレベルや割込みエッジの設定– 割込み要因選択レジスタ• 割込み極性切り替え（両エッジか片エッジか）

2023/09/16

TOPPERSプロジェクト認定110int_switch_1 : EXTI割込み許可• EXTI_IMR：割込み
マスクレジスタ• EXTI_EMR：イベント
マスクレジスタ– EXTI4（ビット4）のイベントクリア割
込みの許可

2023/09/16

TOPPERSプロジェクト認定111

int_swicth_1：EXTI割込み設定• EXTI_RTSR：ライジング・トリガ・
選択レジスタ• EXTI_FTSR：フォーリング・トリガ
・選択レジスタ– EXTI4（ビット４）のライジン
グ・トリガ設定

2023/09/16

TOPPERSプロジェクト認定112

int_switch_1 : NVIC制御関連のレジスタの定義• 割込み制御レジスタ（自動でインクルード）#define TREG_SCB_AIRCR ((volatile unsigned long *)0xE00ED0C)#define TREG_NVIC_ISER ((volatile unsigned long *)0xE000E100)#define TREG_NVIC_ICER ((volatile unsigned long *)0xE000E180)#define TREG_NVIC_IP((volatile unsigned char *)0xE000E400)• EXTIレジスタ（自動でインクルード）#define TREG_SYSCFG_EXTICR ((volatile unsigned long *)(0x40013808)#define TREG_EXTI_IMR ((volatile unsigned long *)(0x40013C00)#define TREG_EXTI_EMR ((volatile unsigned long *)(0x40013C04)#define TREG_EXTI_RTSR ((volatile unsigned long *)(0x40013C08)#define TREG_EXTI_FTSR ((volatile unsigned long *)(0x40013C0C)#define TREG_EXTI_PR((volatile unsigned long *)(0x40013C14)2023/09/16

TOPPERSプロジェクト認定113int_switch_1：外部割込み関係レジスタ定義• 外部割込み制御レジスタ#define EXTI_PortSourceGPIOB (0x01)• 割込み設定定義
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionState;#define INT4_INT_LVL 0x0F

2023/09/16

TOPPERSプロジェクト認定114int_switch_1 : 割込み制御関連のレジスタの初期化• NVICに割込み設定を行うinit_int関数を作成する
void
init_int(unsigned char no, unsigned char mpri, unsigned char spri, FunctionalState active){
unsigned char tmppriority = 0x00, tmpsub = 0x0F, tmpgroup;if(active != DISABLE){
tmpgroup = (*TREG_SCB_AIRCR & 0x700) >> 8;
tmpsub = (tmpgroup + __NVIC_PRIO_BITS) < 7 ? 0: (tmpgroup - 7) + __NVIC_PRIO_BITS;
tmppriority = mpri << tmpsub;
tmppriority |= (spri & ((1 << tmpsub)-1));
tmppriority = tmppriority << (8 - __NVIC_PRIO_BITS);TREG_NVIC_IP[no] = tmppriority;
TREG_NVIC_ISER[no>>5] = 1<<(no & 0x1f); /* 割込みを有効に */}else{
TREG_NVIC_ICER[no>>5] = 1 << (no & 0x1f); /* 割込みを無効に */}}2023/09/16

TOPPERSプロジェクト認定115int_switch_1：SW1用の割込み設定• ポーリング型のSWITCH初期化関数を割込み型に改造• プッシュボタンを押した際に割込みが入るよう立ち下がりエ
ッジかつ片エッジを選択
引数に割込み条件をvoid追加する
switch_push_init(unsigned int no, unsigned int lvl){
unsigned long reg;
unsigned int pinpos = PSW1_PINNO;*TREG_RCC_AHB1ENR |= RCC_AHB1ENR_GPIOBEN;:
reg = 0x04 * (EXTI_PinSource0 & 0x03);
TREG_SYSCFG_EXTICR[pinpos >> 0x02] &= ~(0x0F << reg);
TREG_SYSCFG_EXTICR[pinpos >> 0x02] |= EXTI_PortSourceGPIOB << reg);*TREG_EXTI_IMR &= ~(1<<pinpos);
割込み許可*TREG_EXTI_EMR &= ~(1<<pinpos);*TREG_EXTI_IMR |= 1<<pinpos;*TREG_EXTI_RTSR &= ~(1<<pinpos);
ライジング・エッジ*TREG_EXTI_FTSR &= ~(1<<pinpos);*TREG_EXTI_RTSR |= 1<<pinpos;
init_int(no, lvl, 0x0F, ENABLE);}

2023/09/16

TOPPERSプロジェクト認定116int_switch_1 : データ共有用グローバル変数
メインルーチンと割込みハンドラ間でのデータの共有は，グローバル変数（共有変数）で実現する• LEDのカウントデータはグローバル変数とする
unsigned char led_count;2023/09/16

TOPPERSプロジェクト認定117int_switch_1 : 割込みハンドラ• グローバル変数をカウントアップしてLEDに出力
void
EXTI4_IRQHandler(void){
unsigned long enablestatus = *TREG_EXTI_IMR & (1<<PSW1_PINNO);
if((*TREG_EXTI_PR & (1<<PSW1_PINNO)) != 0 && enablestatus != 0){/* LEDをカウントアップ */led_out_bdigit(++led_count);*TREG_EXTI_PR = (1<<PSW1_PINNO);}}• C言語関数を割込みハンドラとする場合の問題点– 単に関数を割込みハンドラとすると正しく動作しない– 割込みハンドラの出入り口では，元の処理の保存と復帰を行う必要がある
2023/09/16

TOPPERSプロジェクト認定118

int_switch_1 : メインルーチン• 初期化後，割込みを許可して無限ループで割込みを待つ• 割込みの許可はC言語では直接記述できない（プロセッサ毎に命令が異なる）ため，インラインアセンブラによりアセン
ブル言語による記述する– 記述方法はコンパイラによって異なるので注意が必要
void_main(void){
led_init();switch_push_init(INTNO_EXTI4, INT4_INT_LVL);
led_count = 0;
for (;;) {何も行わない}無限ループ}
2023/09/16

TOPPERSプロジェクト認定119スイッチ割込みプログラム2 : int_switch_2 概要• メイン関数でLEDを一定周期でカウントアップし，SW1（EXTI4）が入力されるとカウント値をクリアする• 学習内容– メインルーチンと割込みハンドラ間でのデータの共有– 割込み禁止の必要性（クリティカルセクション）• 動作確認– 作成済みのプログラムを実行し動作を確認する

2023/09/16

TOPPERSプロジェクト認定120

int_switch_2 : データ共有と割込みハンドラ
メインルーチンと割込みハンドラ間でのデータの共有は，グローバル変数（共有変数）で実現する• LEDのカウントデータはグローバル変数とする
unsigned char led_count;• 割込みハンドラではカウントデータをクリアする
void
EXTI4_IRQHandler(void){
unsigned long enablestatus = *TREG_EXTI_IMR & (1<<PSW1_PINNO);
if((*TREG_EXTI_PR & (1<<PSW1_PINNO)) != 0 && enablestatus != 0){/* LEDをクリア */データのクリア
led_count = 0;
led_out_bdigit(led_count);
外部割込み要因のクリア*TREG_EXTI_PR = 1<<PSW1_PINNO;}}
2023/09/16

TOPPERSプロジェクト認定121int_switch_2 : メイン関数• led_dataのカウントアップは外部関数で行う• 外部関数の戻り値（インクリメントした値）を led_data に入れる
void_main(void){
led_init();switch_push_init(INTNO_EXTI4, INT4_INT_LVL);
led_count = 0;

LEDのカウントアップ

for (;;) {led_count = inc_data(led_count);
led_out_bdigit(led_count);
busy_wait();}}2023/09/16

TOPPERSプロジェクト認定122int_switch_2 : カウントアップ関数• 引数の値をインクリメントして返す• busy_wait()により故意に時間を消費している– 関数の実行によりある一定の処理時間が必要であるという想定を模している– 外部関数がライブリラリで提供されていると処理時間は分からない場合がある– busy_wait()は以前のプログラムの２倍の時間待つ
unsigned charinc_data(unsigned char data){
busy_wait();busy_wait();
ダミーウェイトreturn (data + 1);}
2023/09/16

TOPPERSプロジェクト認定123int_switch_2 : プログラムの問題点
前述のプログラムには問題があり正しく動作しない• SW1を押して割込みハンドラでLEDをクリアしても，次のカウントのタイミングで’1’ではなく，前のカウント値をインクリメントした値が表示される

実際に動作させて問題点を確認する• メイン関数ではグローバル変数 led_data を引数に
inc_data() を呼び出し，戻り値を led_data に戻している– グローバル変数の値を読み込んでから書き戻すまでに時間が必要2023/09/16

TOPPERSプロジェクト認定124int_switch_2 : led_countの更新• _main()関数でled_countを読み込んでinc_data()を実行している間に，割込みが発生し，割込みハンドラで
led_countをクリアすると，その後_main()関数によって上書きされてしまう_main()
led_countを引数
に呼び出し

戻り値で
led_count
を更新しLEDに反映2023/09/16

inc_data()

led_count

EXTI4_IRQHandler()

4

割込み発生

0

led_countをクリア
LEDを更新5

TOPPERSプロジェクト認定

値が戻される125

int_switch_2 : 割込みの禁止• 共有データ（led_count）を排他的に扱えば問題は発生しない• _main()関数でled_countを更新する間はSW1の割込みがプ
ロセッサによって受け付けられないようにすればよい– 割込み禁止により実現_main()
led_countを引数に呼び出し

inc_data()

割込み禁止

戻り値で
led_count 割込み許可を更新しLEDに反映2023/09/16

led_count

EXTI4_IRQHandler()
割込み発生

4

割込みが
保留される

5
0
TOPPERSプロジェクト認定

led_countをクリア
LEDを更新126

int_switch_2：割込み許可禁止設定• 割込み許可禁止関数を追加– Enable();
割込み許可– Disable();
割込み禁止
void Enable(void){asm volatile(“cpsie i”);}
void Disable(void){asm volatile(“cpsid i”);}

2023/09/16

TOPPERSプロジェクト認定127int_switch_2 : 割込みの禁止 プログラムでの実現• 割込み禁止・許可命令で実現する– プロセッサ毎に実現方法は異なる– 割込みマスクで実現する方法も（後述）

割込み禁止

2023/09/16

void_main(void){...省略...
for (;;) {Disable();led_count = inc_data(led_count);
led_out_bdigit(led_count);
Enable();busy_wait();}}
TOPPERSプロジェクト認定

割込みが発生しても受け付けられず，割込みハンドラと排他的に実行される128int_switch_2 : クリティカルセクション
排他的に動作する（しなければならない）区間• 実現方法– メイン関数と割込みハンドラ間や割込みハンドラ間は，割込み禁止・許可命令を組み合わせて実現• クリティカルセクションの実行時間– 長くなると，割込みが発生してから応答するまでの時
間（割り込み応答時間）が長くなり，リアルタイムシス
テムでは問題となる場合がある– 修正したプログラムでは，スイッチを押してからクリアされるまでのタイムラグが発生する

2023/09/16

TOPPERSプロジェクト認定129

割込みプログラミング

1. Cortex-Mの割込みアーキテクチャ
2. スイッチ割込みプログラム
3. 割込みタイマ

2023/09/16

TOPPERSプロジェクト認定130

タイマ割込みプログラム : int_timer 概要• タイマ4の割込みを用いて1秒間隔でLEDをカウントアップ
する• 学習内容– タイマ割込みの扱い方• 作成方法– ポーリングプログラム timer をコピーして、ディレクトリ
名をint_timerに修正して作成する

2023/09/16

TOPPERSプロジェクト認定131int_timer : 作成
プログラム int_timer を作成する• 手順– 割込み要因ハードウエアの初期化（タイマ4）• カウントソース，カウント値を適切に設定してスタートさせる– 割込み制御レジスタの初期化• 割込み優先度の設定– 割込みハンドラ• LEDをカウントアップする

2023/09/16

TOPPERSプロジェクト認定132int_timer: init_int関数の取り込み• int_switch_1で作成したinit_init関数をint_timer.cに取り込む• FunctionalState定義も取り込みを行う
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;2023/09/16

TOPPERSプロジェクト認定133int_timer: NVIC制御関連のレジスタの定義• 割込み制御レジスタ（自動でインクルード）#define TREG_SCB_AIRCR ((volatile unsigned long *)0xE00ED0C)#define TREG_NVIC_ISER ((volatile unsigned long *)0xE000E100)#define TREG_NVIC_ICER ((volatile unsigned long *)0xE000E180)#define TREG_NVIC_IP((volatile unsigned char *)0xE000E400)• TIMER4割込みレジスタ#define TREG_TIM4_DIER ((volatile unsigned short *)(0x4000080C)• TIM4_DIER定義#define TIM_DIER_UIE 0x0001 /* update interrupt enable */2023/09/16

TOPPERSプロジェクト認定134int_timer : TIMER4ハードウェアの初期化• オートリロード設定を行えば，一度スタートさせれば，設
定周期で割込みが発生する
void
timer4_init(void){
unsigned short PrescalerValue = 0;:
*TREG_TIM4_EGR = 0x0001;/* Enable TIM4 Preload register on ARR */*TREG_TIM4_CR1 |= TIM_CR1_ARPE;*TREG_TIM4_SR = 0x0000;/* カウントスタート */*TREG_TIM4_CR1 |= TIM_CR1_CEN;/*割込み設定 */*TREG_TIM4_DIER |= TIM_DIER_UIE;
init_int(INTNO_TIM4, 0x0E, 0x0F, ENABLE);}
2023/09/16

TOPPERSプロジェクト認定

オートリロード設定

135int_timer : 割込みハンドラ• 呼び出されると，LEDをカウントアップ• led_countをグローバス変数に修正

unsigned char led_count;void
TIM4_IRQHandler(void){/* 割込みクリア */*TREG_TIM4_SR = 0x0000;
led_out_bdigit(led_count++);}2023/09/16

TOPPERSプロジェクト認定136int_timer：MAIN関数• メイン関数ではled_countをグローバル変数に修正を行った対応
void_main(void){
led_init();timer4_init();
led_count = 0;
for(;;){}}

2023/09/16

TOPPERSプロジェクト認定137

シリアルI/Oの割込み対応• Appendexとしてデバイスの割込み演習を用意しました– シリアルI/Oを割込みで動作させるint_uart• program/int_uartを参考にしてください

2023/09/16

TOPPERSプロジェクト認定

138

まとめ

2023/09/16

TOPPERSプロジェクト認定139

小規模組込み開発のまとめ• 小規模な組込み開発では少数の開発者でファームウェ
ア、ソフトウェアの開発を行うため、ハードウェアの知識や開発環境を構築する知識も必要となる• 開発言語はＣ言語を用いることが多いが、Ｃ言語で記述できない部分は、アセンブラ言語で開発する必要がある• 開発環境はプロセッサメーカーの開発環境を用いることが多く、開発前にノウハウの蓄積が必要• ハードウェアの制御は、ポーリングを用いるのもの、割込みを用いて制御を行うものがあり、適切な方式を選択する必要がある• 割込み制御はプロセッサ毎に異なる。特別な設定を行う必要があり、処理を熟知して開発を行う必要がある
2023/09/16

TOPPERSプロジェクト認定140

小規模組込み開発のまとめ• アプリケーション部分の開発については、オブジェクト指
向やモデル設計を用いれば、可視的なソフトウェア開発ができる。
• このような開発は実行ＲＯＭ、ＲＡＭサイズの増加を伴う
ことが多いので注意が必要• ＮＰＯ法人 組込みソフトウェア管理者・技術者育成研究会では、いろいろは組込みソフトウェア開発手法の紹介を行ってますので、組込み開発プロセスの参考にしてください。http://www.sessame.jp/2023/09/16

TOPPERSプロジェクト認定141

基礎１実習通信講座終了の方へ• my_programには、以下のディレクトリができ正しくプログラ
ムが実行されているはずです• my_programをzip圧縮して、TOPPERS事務局に送り、講座終了判定を行ってください
実習済プログラム
int_switch_1
int_switch_2
int_timer
led_count
led_shiftsampleswitch_push
timer
uart
2023/09/16: 割込みによるプッシュスイッチプログラム: 割込みプッシュスイッチクリティカルセクション検証: タイマー割込みLEDカウントプログラム: ソフトウェアタイマーLEDカウントプログラム: LEDシフトプログラム（コピー）: サンプルプログラム（コピー）: ポーリングによるスイッチ検知: ハードウェアタイマーLEDカウントプログラム: ポーリング方式UART通信TOPPERSプロジェクト認定

142

謝辞
本教材の開発において、NPO法人組込みソフトウェア管理者・技術者育成研究会およびNPO法人TOPPERSプロジェク
トの作成した以下の教材を参考としました．
– OpenSESSAME Seminar組込みソフトウェア技術者･管理者向けセミナー初級者向けテキスト第5版– TOPPERS初級実装セミナー教材
両団体および上記教材の作者の皆様へ感謝します．
本教材の開発において、 NEXCESS初級コースおよび指導者養成コースの受講者の皆様のコメントを参考としました．
両コースの受講者の皆様へ感謝します．

2023/09/16

TOPPERSプロジェクト認定143

著者リスト• メモリマップドレジスタの操作方法の確認– 本田 晋也（名古屋大学）• ポーリングプログラミング– 本田 晋也（名古屋大学）• 割込みプログラミング– 本田 晋也（名古屋大学）• STM32F401-Nucleo用のプログラム作成、テキストの修正– 竹内良輔（（株）リコー）

2023/09/16

TOPPERSプロジェクト認定144