STM32L152 / Application = GPS Logger
 on ( GCC + OpenOCD ) with PN2

IMAGE12.GIF - 2,801BYTES

2012年9月22日 ほぼ完了(完成でなく) RTC関連修正 Gセンサー関連修正 ソースファイルアップデート
2012年9月15日 情報追記ソースファイル公開
2012年9月9日  情報追加だいぶ完成に近づいた(ストップモード中心に更新
2012年9月2日  情報追加相変わらず未完
2012年8月25日 情報追加しかし未完成
2012年8月19日 新規作成

1 はじめに
2 開発環境の再確認 過去の関連情報
3 STM32L152での対応 STM32L-Discovery
3.1 HW構成 GCC+OpenOCD with PN2
3.2 SW構成 その中でSTM32L152はここ
3.3 個別対応/問題点
3.4 その他
4 サンプルプログラム
JTAG(IDCD)でのDebugの様子
LCD制御とJTAG信号が共有されている為、
LCD表示が制限を受けます
DiscoveryボードのSWDを借用してDebug中
こちらは、書込みに時間がかかりますが
LCD制御に問題ありません
2012/7/8更新時、GPSロガーの構想を紹介しましたが、何とか全体が動作するようになりました
(2012/8/19)
まだ充分なDebugが出来ていませんがソースコードを公開しました(2012/9/15)
Bugは残っているでしょうが、ほぼ完了版のソースコードをアップデートしました(2012/9/22)

FreeRTOS上で(→FreeRTOS V7.2.0使用)
RS232C(PCとの通信)、SDカード制御、LCD制御、
アナログ入力(内部温度、バッテリー電圧、右下(赤ダイアル)のTRIM電圧)、
RTC(カレンダー)、スイッチ入力、LED制御
などが動作することを確認しました
GPS接続→動作しました
USB制御→当面、ROM/RAMサイズの関係で検討していません
I2C→これからです
SPIで大気圧センサーとGセンサー →動作しました

を追加していくことが今後の課題で、それらをまとめると定番のGPSロガーとなる

スタンバイ制御(FreeRTOS上でどう制御する?)
→4mA以下のスタンバイ状況から復帰できるようになった(2012/9/2)

復帰方法は下記の様にする                                    
①RTCのアラーム機能で、指定時間後(たとえば1時間後)に復帰(2012/9/9実現)
②WKUPスイッチで任意の時間に復帰(2012/9/2実現)
                
③Gセンサーの揺れ検出で復帰(2012/9/9時点-未完成 2012/9/22実現)   



1 はじめに
IMAGE12.GIF - 2,801BYTES
TOP02_001.PNG - 934BYTES

<STM32Lの魅力>
STM32L-Discoveryを入手し、ねむいさんのぶろぐで知った開発環境を構築してSTM32L152を今年(2012)から使い始めました。
LPC1768を使用してGPSデータロガーを作った時に、このままでは消費電力が大きすぎてポータブル化しても実用性はないと感じました。
そこで昨年はMSP430を勉強したのですが、ROM/RAMサイズの壁に阻まれ撃沈されてしまいました。
そして辿り着いたのがSTM32Lで、MSP430とは違って使いやすく省電力である点が気に入りました。

下記に紹介するGPSデータロガーの例では、STM32Lで60mA程度(含むGPS電流約25mA)です。
4GBのSD(Team MicroSD(HC))から8GB(Transcend)に変更し、LED表示を止めたりしたところ現在は40mAあたりの平均電流で動作しています(2012/9/22)。
LPC1768では150mA以上を消費していました(勿論、入出力等の条件は大きく違いますが)。
但し、STM32Lの省電力機能である動作状況に応じてI/Oを停止させたり、いくつかのlow-powerモードを使いこなすまでには至っていません
が、一様STOPモードを実装し動作させることが出来ました(2012/9/9)。
MSP430のlow-powerモードへの移行と復帰が使い易く非常に魅力的に感じますが、STM32Lを使いこなすためにもう少し勉強を続けてみます。

今回、通常モードとSTOPモードとの状態遷移を制御できるようになったことで一様、初期の目的は達したと思います。
残る問題は、STOP時の電流が4mA程度と大きい点で原因が良く解っていません(2012/9/22)。


次に魅力的なのは、ROM/RAMサイズとCPUパッケージの関係です。
今回は、STM32L-DiscoveryではなくOlimex製STM32-P152を使用しました。
実は慣れ親しんだOlimexのHPが更新されていました。イメージが変わりすぎですが・・・(2012/9/15)
理由は、CPU端子からLCDを直接駆動している為にDiscoveryではSPI、USART、I2Cなどが上手く残りのI/Oにアサイン出来ませんでした。
STM32-P152はCPUが100Pinタイプを使用している為に、残ったI/Oで色々な機能が実現出来ます。
下記を見てください。

CPU型番 ROM RAM Pin数 コメント
STM32L152CB 128K 16K 48 LCD直接駆動しなければこれで充分では?
STM32L152RB 128K 16K 64 STM32L-Discoveryで使用
STM32L152VB 128K 16K 100 STM32-P152で使用
STM32L152VC 256K 32K 100 64,100,144Pinあり
Digi-Keyでは非在庫保有商品扱い
早く最小発注=1で在庫数量!=0で
即時納入に移行してほしいと思います
STM32L152VD 384K 48K 100

STM32L-Discoveryを使用した場合でも付属しているLCDを外して、LCD駆動端子をUSART、SPIなどにアサインすれば今回紹介したGPSロガーも実現出来そうです。
同様に48Pinでも今回の機能を実現出来そうです(今回作成のプログラム移植を検討しています(2012/9/9))。
ピン配置案をこの表にしてみました(まだ案です。出来る保証はありません)。
STM32-P152のLCDも、思ったより表示できる情報は多くありません。
私の好きな、SB1602BAD-12864-SPIを利用すれば、より多くの情報を一度に表示でき且つI/Oも大幅に節約出来ます。

ROM/RAMサイズで言えば、384KB/48KBタイプが魅力です。
Digi-Keyでは、数がまとまった場合のみ入手可能な様子で、残念ながらまだアマチュアには入手が難しいようです。
1個から入手できる様になれば更にSTM32Lの魅力が増すと思います。
MSP430シリーズの省電力も魅力ですが、384K/48Kのような製品をTIが簡単にアナウンスするとは思えません。
現時点(2012年8月19日時点)で、Digi-Keyで検索すると、

CPU型番 ROM RAM Pin数 コメント
MSP430F5438IPZ 256K 16K 100 一個から入手可能なようですが・・・

が最大です。



<今回のロガーについて>
コンセプト自体は、2010年に製作したPIC24を使ったGSP Data Loggerと大きく変わりません。
その年の後半にLPC1768を使って製作したNXP LPC1768 Application on FreeRTOSもGPSロガーで今回の仕様に似ています。

概要をまとめると下記の様になります。

項目 仕様/モジュール コメント
使用基板 Olimex STM32-P152 汎用拡張エリアに手配線で追加
使用CPU STM32L152VBT6
100pin, 128KB(ROM)/16KB(RAM)
.
GPS SiRF3 type GPSモジュール
SZP950T (SzParts.com)
TTLレベルで外部設置型
RS232 I/F 基板上に実装済
(ドライバーICの電源ON/OFF制御可能)
ON/OFF機能はSWで設定したが、
まだONしたままで何もせず
USB 基板上に実装済(但し、今回未使用) .
SD-Card Micro-SDカード用変換基板(サンハヤト)
CK40
汎用拡張エリアに手配線で追加
SWはFAT32で使用しているので、大容量も使用可
現在、4GB使用中
G Sensor 3軸加速度センサーモジュール
KXP84-2050 (秋月電子通商)
SPIインターフェイスで接続
Barometer 気圧センサーモジュール
SCP1000-D01 (秋月電子通商)
SPIインターフェイスで接続
(D01がSPI用で、D11がI2C類似I/F。秋月製品はSPI)
3.3V Reg. 東芝3.3Vロードロップアウトレギュレータ
TAR5S33 (秋月電子通商にて入手)
GPSとSDカードの電源(3.3V)をCPUから
ON/OFFして制御(但し、現時点ではONしたまま)


2 開発環境の再確認
IMAGE12.GIF - 2,801BYTES
TOP02_001.PNG - 934BYTES

<最新の環境にしていません>
参考にさせていただいた、ねむいさんのぷろぐではOpenOCDや付帯ツールに関してアップデートが行われています。
最新情報を入手して開発環境をアップデートすると良いでしょう。
下記のところは是非訪ねてみてください。


しかし、私の場合にはここで構築した環境を変えずにそのまま使用しています
開発環境を変えていないのは、GPSロガー製作へ集中するためです。
少し落ち着いたら、開発環境をアップデートしたいと思います。



3 STM32L152での対応
IMAGE12.GIF - 2,801BYTES
3.1 <HW構成>
TOP02_001.PNG - 934BYTES

1.使用ボードの回路図
 Olimexのサイトに回路図があります
 私が購入したストロベリー・リナックスでも入手可能です。



2.追加回路
 この結線表を参照ください。
 一部修正しました。
 修正箇所は、PA4に接続されているLED(STAT1)の駆動トランジスタのベース端子を外して、大気圧センサーの電源に接続し、ON/OFF制御を追加(2012/9/2)。
 
GセンサーのMOT信号でSTOPモードから通常モードへの復帰を考えたが、FF(Free-fall Detect)の端子に変更した(2012/9/22)。



3.SD Card結線

No. 信号名 CPU側結線先 コメント
1 DAT2 N.C. 未接続
2 DAT3/CS PE12 (OUTPUT) ソフトウェアでCSをHi/Lo制御
3 CMD/DI SE15/SPI1_MOSI .
4 VDD VCC(+3.3V) PE5の端子で3.3V電源をON/OFF制御
5 CLK PE13/SPI1_SCK .
6 VSS GND .
7 DAT0/DO PE14/SPI1_MISO .
8 DAT1 N.C. .
. FG GND .
. WP N.C. ポートの空きがあれば、検出すべき
. INS N.C. ポートの空きがあれば、検出すべき




4.GPS結線

電線色 信号名 CPU側結線先 コメント
VCC VCC(+3.3V) PE4の端子で3.3V電源をON/OFF制御
バックアップ電源 Vbat 規格は1.8-3.6Vですが、現在5Vに接続
問題→修正要
 直列にダイオード2本入れてみました
 (実測3.2V)
データ出力 PC11/USART3_RX .
データ入力 PC10/USART3_TX .
GND GND .
(シールド) GND GND .




5.端子の処理と不足のI/Oの補完対策
 STM32-P152の回路図には、スタンバイ時の電力を最小にするために工夫がしてあります。
 下記は、TRIM(基板上にある可変抵抗器)のADC入力の例です。

 10Kオームの抵抗を接続したままにすると330μAが常時流れてしまいますが、TRIMER_ENをCPU端子に接続しADC変換する時のだけGNDに落として計測します。
 こうすれば、常時無駄な電流を流さずに済みます。 
 バッテリー電圧のADC計測も同様な方法で対応しています。
 逆を言うと、このボードにはスタンバイ用の別電源がなく、ソフトウェアでCPUをスタンバイもしくはストップさせて待機させ、Wake-up端子やRTCのwakeup起動で正常復帰を目指します。
 その為に、RS232用のドライバーICの電源をON/OFFする機能も搭載させています。
 
 このように工夫されたボードでSTM32Lの省電力を最大限に発揮するように設計されていますが、LCDに46本のCPU端子が接続されていて、100PinのCPUの多くを費やしています。
 その為に、自由になる端子がそれほど多くありません。
 そこで、下記の様に一つの端子を共有して使用しています。

No. 端子名 信号名 共有目的 コメント
1 PD1 GPIO
SPI2_SCK
1)SPIのSCLK出力
2)TRIMER_ENとしてのLow出力
2つの機能を動的に変更予定
現在はSCLKとしてのみ制御し、クロックが発生していない時にLowレベルになっているので、成立している
2 PD7 GPIO 1)KXP84(G-sensor)のCS制御
2)SENSE_EとしてのLow出力
SPIのCSを優先
結果として個別にLowを作って成立している
3 PD3 USART2_CTS
SPI2_MISO
今回は共有せずにSPI2_MISOとして使用 HWとしてR43を取り除いてMISOとして使用
RS232のハードウェアフロー制御は出来ない
4 PD4 USART2_RTS
SPI2_MOSI
今回は共有せずにSPI2_MOSIとして使用 HWとしてU4の10ピンを浮かしてMOSIとして使用
RS232のハードウェアフロー制御は出来ない
5 PB3 TDO
SEG7
JTAG端子としての使用を止めた
SWDだけでDebugを実施
開発当初、JTAGポートでDebugを開始したが、LCD表示が上手く動作せずソフトウェアのBugとして悩んだ

しかし回路図をよくチェックすると、LCD制御とJTAG端子が共有されていることが原因と判明
6 PB4 TRST
SEG8
JTAG端子としての使用を止めた
SWDだけでDebugを実施
7 PA15 TDI
SEG17
JTAG端子としての使用を止めた
SWDだけでDebugを実施




3.2 <SW構成
TOP02_001.PNG - 934BYTES

3.2.1.FreeRTOS
 FreeRTOSは、ここで説明しているSTM32L-Discovery用に修正したものを使用しています
 本家のFreeRTOSのVersionは、7.2.0(2012/8/19現在)になっています。
 8月25日にFreeRTOSのVer.7.2.0をダウンロードし、差分を簡単にチェックしたところ移行に大きな問題がなさそうなので、Ver.7.2.0にしました。
 結果として大きな修正を加えずにFreeRTOSは動作しましたが、Debugは紆余曲折で1ヵ月くらい悶々としながら週末Debugする羽目になりました。
 修正箇所は下記の様になっています。

Directory 修正有無 コメント
\Source なし Ver7.2.0に移行
\Source\portable\GCC\ARM_CM3 なし Ver7.2.0に移行
\Source\portable\MemMang なし Ver7.2.0に移行
\Source\include あり Ver7.2.0に移行
\Demo\CORTEX_STM32L152_IAR あり ここはmain.cを含め、ユーザー依存
\Demo\CORTEX_STM32L152_IAR\system_and_ST_code あり 別途STMicroelectronicsから
Laibrary入手
\Demo\Common\include なし Ver7.2.0に移行
\Demo\Common\Minimal なし Ver7.2.0に移行
\Demo\Common\FileSystem あり ChaNさんのサイトからR0.09を入手





3.3..2.GPS受信
 GPSは電源ON/OFFを行うことが出来ますが、現時点(2012・8・19)では積極的に制御出来ていませんSTOPモードへ入る際に電源を切断しています(2012/9/9)。
 Gセンサーの出力が変化しなくなった時(静止時)には、1秒毎の更新を止めて時間間隔を延ばして記録すると更に長時間記録が出来ると考えています。
 がスタンバイ機能と合わせて今後の課題ですGセンサーの出力を静止状態と判断した時はSTOPモードに入れています(2012/9/9)。
 GPSとは4800BPSで通信しています。
 起動後、GPSから送信してもらう情報を下記のコマンドの中から必要なものを選択して送信し、設定しています。

/* Commands for GPS to turn on or off data strings */
#define RMC_ON "$PSRF103,4,0,1,1*21\r\n"
#define RMC_OFF "$PSRF103,4,0,0,1*20\r\n"
#define GGA_ON "$PSRF103,0,0,1,1*25\r\n"
#define GGA_OFF "$PSRF103,0,0,0,1*24\r\n"
#define GSA_ON "$PSRF103,2,0,1,1*27\r\n"
#define GSA_OFF "$PSRF103,2,0,0,1*26\r\n"
#define GSV_ON "$PSRF103,3,0,1,1*26\r\n"
#define GSV_OFF "$PSRF103,3,0,0,1*27\r\n"

 その後は、送られてくるデータをそのまま受信してMicro-SDへ格納しています。
 受信データは、最初はFreeRTOSのQueueで渡したがリングバッファに切り替えた。
 理由は取りこぼしがあったためで、リングバッファの方がQueueより処理が軽いことが伺えます。

 cChar = USART_ReceiveData( GPS_COM );
 xQueueSendFromISR( xRxedChars, &cChar, &xHigherPriorityTaskWoken );
Queueを使って受信データを受け渡し
 cChar = USART_ReceiveData( GPS_COM );
 ring_putc(&ring_rx_pc, cChar);
同じことをRing Bufferで

 GPSデータから下記のようないくつかデータを抽出しています。

処理 処理関数 内容
時間抽出 gps.c
void get_time( RTC_TimeTypeDef *pt )
UTCベースの時間取得
詳しくはRTCの項で解説
位置情報 gps.c
void convert_lat( GPS_LatTypeDef *pl )
void convert_lon( GPS_LonTypeDef *pl )
緯度経度情報
GPSのデータは、
43"41'32.23N
142"30'49.79E
の形式だが、これを

の形式に変換する
衛星数 gps.c
void convert_lnf( GPS_InfTypeDef *pl )
信号を受信している衛星の数
標高 .
移動速度 ノットでの速度表示をメートル変換





3.2.3.SD-Card制御
 ChaNさんのFatFs(R0.09a)とねむいさん作成のMMC Driver ( mmc_stm32l.c )を使用させていただきました。
 プログラムは一部修正して使用しています。
 エラー処理を追加しました。
 SDカードが入っていない場合や書込み不良になった場合には、エラーに起因してSTOPモードに移行します(2012/9/22)。

出典元 変更点
 FatFs修正
 (ChaNさん)
→ファイルサイズ拡大
 考え方はここに記述しています

typedef unsigned long long DDWORD;

/*DWORD fsize; */ /* File size */
DDWORD fsize; /* File size */

/*fp->fsize = LD_DWORD(dir+DIR_FileSize);*/
fp->fsize = LD_DDWORD(dir+DIR_FileSize);

2012/8/27付けで、R0.09からR0.09aに更新されましたが、
内容をチェックして
いません(2012/9/2)新しいVerへ移行しました(2012/9/15)
 mmc_stm32l.c修正
 (ねむいさん)
I/Oポートの変更
FreeRTOS上でDelay関連をvTaskDelay()使用に変更
DMAモードに設定しました
安定して動作しています(2012/9/2)

 両氏に感謝いたします。





3.2.4.LCD制御
 今回、LCD制御のDebugが一番大変でした。
 初めにJTAG使用でLCD表示が乱れたことをBugと思ってしまい、ソースコードとにらめっこする羽目に陥りました。
 これは、Segment信号とJTAG信号が同じ端子を共用している問題だと判明し一見落着。
 次は、参照したソースファイル
  Demo software USB mouse and Blinking LED for EW-ARM (Olimex Website) ->Demos_STM32-P152_v1.00.zip
 の中にあった
  \Demos_STM32-P152_v1.00\Demos\Projects\bsp\stm32_glass_lcd.c
 をLCD制御のベースとし、更に機能追加して使用させてもらったが色々な問題が発生してしまった。

BUG症状 間違い 修正 コメント
SEG5ラインのセグメントが表示されない
具体的には、右上の4桁7セグメントの一番右
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_0 | GPIO_Pin_1 |
GPIO_Pin_4 | GPIO_Pin_5 |
GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_11 |
GPIO_Pin_12 | GPIO_Pin_13 |
GPIO_Pin_14 |GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 |
GPIO_Pin_8 | GPIO_Pin_9 |
GPIO_Pin_10 | GPIO_Pin_11 |
GPIO_Pin_12 | GPIO_Pin_13 |
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init( GPIOB, &GPIO_InitStructure );
GPIO_Pin_3が抜けていた
→これは私自身がソースファイル修正中に仕込んだBug
14セグメント左端桁のJセグメントが表示されない LCD->RAM[LCD_RAMRegister_1] &= (uint32_t)(0xFFFFFF0F);
-----
LCD->RAM[LCD_RAMRegister_7] &= (uint32_t)(0xFFFFFF9F);
/* Write the corresponding segments (SEG36, SEG37, SEG38, SEG39) */
tmp = S14_FontMap[offset];
LCD->RAM[LCD_RAMRegister_1] |= (tmp << 4) & 0x00F0;
-----
LCD->RAM[LCD_RAMRegister_7] |= (tmp >> 8) & 0x0060;
LCD->RAM[LCD_RAMRegister_1] &= (uint32_t)(0xFFFFFF8F);
-----
LCD->RAM[LCD_RAMRegister_7] &= (uint32_t)(0xFFFFFF8F);
/* Write the corresponding segments (SEG36, SEG37, SEG38, SEG39) */
tmp = S14_FontMap[offset];
LCD->RAM[LCD_RAMRegister_1] |= (tmp << 4) & 0x0070;
-----
LCD->RAM[LCD_RAMRegister_7] |= (tmp >> 8) & 0x0070;
これは出典ソースファイルの問題

このDebug中のLCD表示に問題が
見える
TASKと表示できていない
個別のスペシャルキャラクターが上手く表示出来ない 詳細は割愛 . 私のミス

 LCD制御は、かなりソースコードを読みこなさないと理解できない部分が多く、苦労しました。
 下記に私の理解した制御の流れを簡単に記述してみた。

処理順序 処理 処理内容
初期化1 IOポートをLCD制御
端子として初期化
LCD_GPIOConfig()
① LCDで使用するIポート(GPIOA, GPIOB, GPIOC, GPIOD,GPIOE)のクロックをEnable
② 各Pin毎にAFとして設定
 例えば、
 GPIO_PinAFConfig( GPIOA,GPIO_PinSource8, GPIO_AF_LCD );
 で、PA8をCOM0として使用
③ 各Pinのクロック、出力ポート形態、プルアップダウインの有無設定
初期化2 LCD制御回路の
初期化
LCD_GLASS_Init()
LCD機能ブロック内にある各レジスタの設定
① LCDのクロック設定→LCDクロックはRTCクロックと同じものが使われる
 今回は、RCC_RTCCLKConfig()でLSEすなわち外部に接続したXtal=32.768KHzを選択している
② LCDの基本設定(プリスケーラ、デバイダ、デューティ、電圧源)を実施
 stm32l1xx_lcd.c内LCD_Init()を呼ぶことで実現
 今回は、次の設定で使用
 PS=2,DIV=20 ->f-frame=56.8Hz
 1/4 duty, 1/3 bias
③ 追加設定 (但し下記設定がどんな目的だか未だ良く解っていない(出典のまま))
LCD_MuxSegmentCmd( DISABLE );
LCD_HighDriveCmd( ENABLE );/* High drive enable */
/* Configure the Pulse On Duration */
LCD_PulseOnDurationConfig( LCD_PulseOnDuration_2 );
/* Configure the LCD Contrast */
LCD_ContrastConfig( LCD_Contrast_Level_1 );
④ LCD機能をEnabale
 LCD_Cmd( ENABLE ); 
動作1
(準備)
LCDとCPUの
ハードウェア結線
<LCD自身>
 STM32-P152に使われているLCDはOlimex特注の物でOLIMEXという表示も可能になっている
 ここで、簡単な仕様が確認出来る
 COM端子が4つ、Segmentが42端子あり、個別に4x42=168ドットのON/OFFで表示が成立する
<LCD用RAM>
 STM32L152内にある表示バッファは二重構造となっている
 ユーザーからはLCD_RAMしか見えないが、表示用にはLCD_DISPLAYがある
 両方のシンクロには、UDR(uodate request)とUDD(update done)のフラグを使用する
 LCD_RAMは、64bit長単位で8個用意されているが今回のLCDではそのうちの4個を使用
<LCDとLCD_RAMとの関係>
 COM端子4つのそれぞれCOM0、COM1、COM2、COM3がLCD_RAM0、1、2、3に対応
 Segment端子42個がそれぞれの各Bitに割付けられている
 従って、COM0(LCD_RAM0)のBit0をONすれば、それに対応したLCDのある箇所が黒く見える
<LCDとCPU結線>
 LCDのCOM端子とCPUのCOM端子は4本それぞれに対応して結線される
 SEGは、LCD側42本に対して、CPU側は最大44本を駆動可能である (COM4 x SEG44 →100pinで対応可)
 SEG端子に関してはLCDのどの端子をCPUのどの端子に結線するかは任意であり、P152ではFig.1の様結線されている
 これで判ることは、COMはCPU,LCDで一致し、LCDのPIN1(1A,1B,1C,1D)はCPUのSEG37に結線されており、
 PCBレイアウトの引き回し優先となっている様子
動作2 キャラクタ表示 <左端14セグメントに 'D' を表示する場合を例として>
① void LCD_GLASS_DisplayChar(uint8_t* ch, Point_Typedef point, Semicol_Typedef semicol, uint8_t position)を呼ぶ
 引数は、ch='D'、point=0(小数点表示(今回無し))、semicol=0(セミコロン(無し))、position=0とする
② LCD_FLAG_UDRをチェックしてLCDデータをアップデート出来るか確認(出来るようになるまで待つ)
③ 次にLCD_GLASS_WriteChar( ch, point, semicol, position )を呼ぶが上記引数のまま
④ LCD_GLASS_WriteChar内で先ずposition別でswitch文分岐する
 今回はposition=0で左端選択(全体で7桁)
⑤ 次にLCD_RAMのどのビットが一桁目(position=0)の各セグメントに対応しているか判断し、先ずは全てクリアする
 すなわち、1A、1B、1C、1D、1E、1F、1G、1H、1J、1K、1M、1N、1P、1Qの各対応するビットをクリアする(Fig.2参照)
 そこで、LCD_RAMのどこに対応するかを先ず特定する必要がある
 Fig.1とFig.2から、
 LCD端子では、Pin1、Pin2、Pin43、Pin44で、CPU側ではそれぞれSEG37、SEG36、SEG39、SEG38に対応するので
 Fig.4から
 SEG36 = bit4 = 0x10 SEG37 = bit5 = 0x20  SEG38 = bit6 = 0x40  SEG39 = bit7 = 0x80
 となる
 COM0では、1N、1D、1Pとマイナス(MINUS)が対応し(Fig.2 COM0と同じ行を見て探す)
 COM1は、1M、1C、1Q、1E
 COM2は、1K、1B、1G、1F
 COM3は、1J、1A、1Hとプラス(PLUS)となるがビットの位置は全て同じBit4,5,6,7に対応する
 LCD_RAMが、COM0,1,2,3それぞれに対応してそれぞれをクリアするので
 /* 1N,1D,1P,'-' */
 LCD->RAM[LCD_RAMRegister_1] &= (uint32_t)(0xFFFFFF8F); /* Ignore '-' */
 /* 1M,1C,1Q,1E */
 LCD->RAM[LCD_RAMRegister_3] &= (uint32_t)(0xFFFFFF0F);
 /* 1K,1B,1G,1F */
 LCD->RAM[LCD_RAMRegister_5] &= (uint32_t)(0xFFFFFF0F);
 /* 1J,1A,1H,'+' */
 LCD->RAM[LCD_RAMRegister_7] &= (uint32_t)(0xFFFFFF8F); /* Ignore '+' */
 とすることでクリア出来る
 (LCD_RAMは先に64bit長単位で8個と表現したが、ここでは32bit単位で16個表現で使用している)
⑥ その次に、キャラクタ 'D' を表示する為にどのセグメントをONとするかを決定する
 ROM内に置いた配列 S14_FontMap[] で先ず 'D' のデータを取得してくる
 データはS14_FontMap['D']で取得出来る
 このデータはビット毎に各セグメントのON/OFFに対応している
 並びは、
 Bit 15,14,13,12, 11,10, 9, 8, 7, 6, 5, 4,  3, 2, 1, 0
     0, H, A, J,   F, G, B, K,  E, Q, C, M, 0, P, D, N
 となり、
 'D' ではFig.4のセグメントに相当するので
    0, 0, 1,1    0, 0, 1, 0  0, 0, 1,0   0, 1, 1, 0 = 0x3226
 これは、
    COM3     COM2    COM1    COM0
 の各ビットの並びと対応していることに注意のこと
 そこで、このデータを先に一桁全部クリアした部分に埋め込むことになる。
 /* RAM(reg0) bit4 = tmp bit0 = 1N & bit5 = tmp bit1 = 1D bit6 = tmp bit2 = 1P & bit6 = Ignore */
 LCD->RAM[LCD_RAMRegister_1] |= (tmp << 4) & 0x0070;
 /* RAM(reg0) bit4 = tmp bit4 = 1M & bit5 = tmp bit5 = 1C bit6 = tmp bit6 = 1Q & bit7 = tmp bit7 = 1E */
 LCD->RAM[LCD_RAMRegister_3] |= (tmp << 0) & 0x00F0;
 /* RAM(reg0) bit4 = tmp bit8 = 1K & bit5 = tmp bit9 = 1B bit6 = tmp bit10 = 1G & bit7 = tmp bit11 = 1F */
 LCD->RAM[LCD_RAMRegister_5] |= (tmp >> 4) & 0x00F0;
 /* RAM(reg0) bit4 = tmp bit12 = 1J & bit5 = tmp bit13 = 1A bit6 = tmp bit14 = 1H & bit7 = Ignore */
 LCD->RAM[LCD_RAMRegister_7] |= (tmp >> 8) & 0x0070;
 少し判りずらいのがデータシフトの方向と数で、ビットの並びとCOMxとで関連付けていることに起因する
 Bit4,5,6,7の場所にデータを埋め込む為には、
 (tmp << 0) →COM1抽出(シフトせず)
 (tmp << 4) →COM0(Bit0,1,2,3)抽出
 (tmp >> 4) →COM2(Bit8,9,10,11)
 (tmp >> 8) →COM3(Bit12,13,14,15)
 となる
⑦ LCD_RAM内にデータがセット出来たら、LCD_UpdateDisplayRequest()を呼び出してLCD_DISPLAYへデータを転送する

LCD to CPU connection
LCD Pin assignment
CPU LCD_RAM Register





3.2.5.SPI制御
 SPIポートは今回、SPI1とSPI2を使用している。
 SPI1はSDカードのファイル制御に使っており、「3.SD-Card制御」に簡単な説明を記述しました。

 SPI2は、G-sensorとBarometerの2つのセンサーをCSを個別設定し、SCK、MISO、MOSIを共有して制御している。
 関連するファイル類は、下記の様になる。

ファイル名 機能
spi_sensor.c
spi_sensor.h
SPI2のポート割付け
G-sensor関連の操作関数
Berometer関連の動作関数
src/stm32l1xx_spi.c
src/stm32l1xx_spi.h
STMicroelectronics作成の標準ドライバ

3.2.5.1 G-sensor
 KXP84-2050 (秋月電子通商)は、3軸±2Gまで測定可能なKionix社製加速度センサで、I2Cでも制御出来ますが今回はSPIインターフェイスで動作させています。
 データは個別に読出しも出来ますが、今回は3軸のデータを一括して読み込んでいます。

void spi_Gsen_read_data( void ){
uint16_t dt0;

  G_SEN_CS_LO();   /* CS assert */
  Spi_sensor_wr_byte( XOUT_H );   /* read 16 bit G-X sensor data */
  vTaskDelay( 1 / portTICK_RATE_MS ); /* Wait */
  dt0 = Spi_sensor_rd_byte() << 8;
  dt0 += Spi_sensor_rd_byte();
  Gsen_x_data = dt0 >> 4;
  dt0 = Spi_sensor_rd_byte() << 8;  /* read 16 bit G-Y sensor data */
  dt0 += Spi_sensor_rd_byte();
  Gsen_y_data = dt0 >> 4;
  dt0 = Spi_sensor_rd_byte() << 8;  /* read 16 bit G-Z sensor data */
  dt0 += Spi_sensor_rd_byte();
  Gsen_z_data = dt0 >> 4;
/* Read resiters */
Ff_int = Spi_sensor_rd_byte();
Ff_delay = Spi_sensor_rd_byte();
Mot_int = Spi_sensor_rd_byte();
Mot_delay = Spi_sensor_rd_byte();
Ctrl_regc = Spi_sensor_rd_byte();
Ctrl_regb = Spi_sensor_rd_byte();
Ctrl_rega = Spi_sensor_rd_byte();
  G_SEN_CS_HI();   /* CS negate */
}

 残務としては(これが大きいが)、
  ① 静止状態を判別し、移動していない場合にはGPS Loggerの動作を間引くなり、スタンバイ状態とする(一様実現出来た(2012/9/9))
  ② スタンバイ状態からの復帰にMOT信号を用いてWake-up動作をさせる(2012/9/9時点→未完)
 ということで②がまだソフトウェア作成に着手していません(2012/9/15)。

 <2012/9/22記>
 本日までに、Gセンサーからの信号変化(FF(Free-Fall Detect)とMOT(Motion Detect))をSTOPモード中に検出して、通常モードへ復帰が出来るようになりました。
 当初は、MOT端子をCPUの割込みラインに接続する予定でしたが、FF端子の出力はレジスタ設定によってMOT信号とGセンサー内でORしてから出力できることが判ったので、FF端子を使用しました。
 サンプルプログラムではMOT信号の処理として書かれていますので、注意願います。
 これで、Gセンサーの信号を基に通常モードとSTOPモードとの間で突入/復帰が出来るようになり、電源を使用状況によって自動的に制御出来るようになりました。
 GPS Loggerが移動状態ではLoggingが継続され、静止状況に入れば一定時間後にSTOPし、更に動き出せば自動復帰します。

 今回も色々と苦労したので、KXP84-2050をSPIモードで使用する際の気になる点をまとめてみました。 

項目 ポイント
データ読出し(1) センサーからのデータは、個別に読みだすより、一括読出しが便利

マニュアルにも、
The KXP84 auto-increments register transmits on SDO.
Therefore, Y-axis, Z-axis, CTRL_REGA, CTRL_REGB, and CTRL_REGC will follow
the two X-axis bytes automatically.
及び
Recommend reading X-axis, Y-axis, Z-axis, and the three Control Registers
for each read cycle to verify the mode selections and status
と書かれて推奨されています
上のspi_Gsen_read_data()の赤字部分を追加
データ読出し(2) データ読出しのタイミングで注意する点

マニュアルには、
There should be a minimum of 200μs between the first and second bytes
in order to give the A/D conversion adequate time to complete.
とあり、各軸のGデータを読む場合、アドレスを書き込んだ後に200μSの間隔を置くことが
要求されています
FF/MOT信号の
初期設定
使いこなしているか?→まだよく解らない?
FF/MOTの信号をORしてFF端子から出力出来る

MOTについて
MOTは運動状態が高いGで一定時間続いた場合に、信号がアクティブとなる
2つの条件があり、①Gレベルの閾値(MOT_INT)、②継続時間(MOT_DELAY)をレジスタ値として
設定してイベントを待つ
今回の設定値は、±1.2gが20mS以上継続した場合にFF端子が変化し、保持される
(①MOT_INT(Reg)=61 ②MOT_DELAY(Reg)=5 ③CTRL_REGC:MOTLatch bit=1)
実際に信号をセンサーから出す為には、
MOT信号をFF端子に出力するように設定し(CTRL_REGB:FFMOTI=1)
割込み許可(CTRL_REGB:MOTIen=1)
を行う

FFについて
MOTと同様にFF_INTとFF_DELAYを設定する
閾値は、今度は落下状態の様に一定時間、小Gが続いた時に信号がアクティブとなる
今回の設定値は、±0.5gが20mS継続した時にFF端子に出力される
Read/writeで
アドレスが違う
読出しモードではアドレスのMSBが1で、書込みモードではMSBが0となる
いつもの早とちりで、レジスタ値が設定出来ないことで週末悩む結果となった

マニュアルには、
The MSB of this command indicates if you are writing to (0) or reading from (1) the register.
とある
FF端子の変化を
捕える
GセンサーがMOT(高G)もしくはFF(小G)を検出した場合には、STOPモードで休止していても
WAKEUPしてGPS LOGGERとして、再び記録再開する
その為に今回はFF端子をPE6(WKUP3)に接続し、外部割込み信号(EXTI6)とCPU内で接続し、
STOPモード時にEXTI9_5_IRQHandlerの割込みが起動するように準備しておく





3.2.5.2 Barometer
 同じラインでCSのみ違うだけですが、当初このセンサーからのデータがうまく読めませんでした。
 ハンダ付けがうまく出来ていなくてMISOラインが浮いていたのが一因ですが、直した後でもやはり動作しませんでした。
 結構Debugに時間がかかったのですが、結果として下記のようなことが判明しました。

/* Receive data with sending dummy data(0x00) */
uint8_t Spi_sensor_rd_byte( void ){
  /* Send byte */
#if 0
  /* 0xff doesn't work for Barometer but works well G-sensor */
  SPI_I2S_SendData( SPI2, (uint16_t)0xff ); /* Hiを送信した場合には受信データが上手く取れない */
#else
  SPI_I2S_SendData( SPI2, (uint16_t)0 );
#endif
  /* Wait to receive a byte */
  while ( SPI_I2S_GetFlagStatus( SPI2, SPI_I2S_FLAG_RXNE ) == RESET ) { ; }
  /* Read from the SPI bus */
  return (uint8_t)SPI_I2S_ReceiveData( SPI2 );
}

 受信時にダミーデータを送出しながら、8個のCLKを発生させMISOラインを読み込んでいますが、
  ダミーデータは0xffではうまく動作しない
  ダミーデータが0であれば動作する

 という結論です。
 ちなみにこの関数は、G-sensorでも使用しているがG-sensor側でのデータ受信は0xffでもゼロのどちらのダミーデータでもうまくデータ受信できる。

CPU->Barometer
 0x1c ( STATUS(0x7f) << 2 + RDF(0x0) + 0 = 0x1c )
Barometer->CPU
 0x3e ( bit5=1->DRDY=1->Data is ready, bit4=1->RTERR=1->Real time error (割込みをタイムリーに処理しなかった→無視), bit3,2,1=1-> Reseved, bit0=0->STARTUP=1->startup proc->finished )
CPU->Barometer
 0x84 ( TEMPOUT(0x21) << 2 + RDF + 0 = 0x84 )
Barometer->CPU
 0x026f ( 14bit temperature data)
CPU->Barometer
 0x7c ( DATAR8(0x1f) << 2 + RDF + 0 = 0x7c )
Barometer->CPU
 0x06 ( Pressure data (MSB) )
CPU->Barometer
 0x80 ( DATA16(0x20) << 2 + RDF + 0 = 0x80 )
Barometer->CPU
 0x270c ( Pressure data(LSB) )

    上記例では
    温度データ 0x26f 気圧データ 0x6270c





3.2.6.RTC制御
 GPS Loggerで時計が正確でないのは情けないですが、GPSデータからシステムのRTCを設定し継続して制御するのは結構難しことです。
 制御の流れは下記の様になります。

処理の流れ 関数とそのファイル名 処理のポイント
パワーオン後、
GPSデータが正確な時間情報を
含み始めた場合にRTCを
初期化する作業を開始する
main.c
 void vGPS_Task( void *pvParameters )

real_time_clock.c
 void set_GPS_to_RTC( void )
パワーオン後は、
flg.BIT.time_sync=0
の状態であり、この状態で、
Gps_state == GPS_DAT_RDY
となれば(GPSからのデータに日付け、時間情報が確定した状態)、
set_GPS_to_RTC()
を呼出し、RTCをGPSデータに基づき設定する
GPS内RMC情報からUTC時間を抽出 real_time_clock.c
 void set_GPS_to_RTC( void )
 get_utc_data_from_GPS()
 get_time( &UTC_Time )
 get_date( &UTC_Date )
ASCII文字列からUTCデータを取得して、下記構造体形式で格納
typedef struct
{uint8_t RTC_Hours; uint8_t RTC_Minutes;
uint8_t RTC_Seconds;uint8_t RTC_H12;
}RTC_TimeTypeDef;
typedef struct
{uint8_t RTC_WeekDay; uint8_t RTC_Month;
uint8_t RTC_Date; uint8_t RTC_Year;
}RTC_DateTypeDef;
UTC時間をJSTへ変換 real_time_clock.c
 convert_UTC2JST()
+9時間でJSTに変換
但し、下記の状態をチェック
①同じ日に収まらない時は要注意
②日にちを+1して月の最終日を越えていないことをチェック
③2月がどうかチェックし、2月29日ならうるう年チェックして
 月をまたぐか判断
④2月以外は月を越えたら、次の月に
⑤12月なら年を+1とし、1月1日に
JST時間に基づいてRTCを初期化 real_time_clock.c
 void set_GPS_to_RTC()

stm32l1xx_rtc.c
 RTC_SetTime()
 RTC_SetDate()
.
RTCの時間とGPSとの時間差をチェックし、
差がある閾値を超えた場合には
再度GPSデータに基づきRTCを再補正
main.c
void vGPS_Task( )
.





3.2.7. スタンバイ制御(STOP MODE)
 <2012/9/9記>
 本日までに、パワーダウンモードへの移行と通常モードへの復帰がFreeRTOS制御上で動作するようになった。

 3.2.7.1 STM32L152の"low-power modes"
 STM32Lでは、下記のような省電力モードを利用することが出来る。

モード名 CPU FLASH RAM IO CLOCK LCD RTC 備考
RUN 動作 動作 動作 動作 全ての
クロック
ソースを
選択可
動作 動作 通常動作
このモードでもクロックの最適化、
不要なIOを非動作とすれば省電力は可能
Low power run 制限付き
動作
動作/
非動作
動作 動作 MSI 動作 動作 クロックを低速にして、CPU内部電源を低電圧に
FLASHを非動作として、RAM上でソフトウェアを
動作させることも可能
Sleep 非動作 動作 動作 動作 全ての
クロック
ソースを
選択可
動作 動作 割込みやイベントを待っている時にCPUを止める
Low power sleep 非動作 非動作 動作 動作 MSI 動作 動作 Low power runからの移行に
便利なモード?
Stop with RTC 非動作 非動作 動作 事前状態
保持
LSI/LSE 動作 動作 LCDとRTCを動作させて
且つ最少の消費電力で保持
Stop without RTC 非動作 非動作 動作 事前状態
保持
LSI/LSE 非動作 非動作 RAM内容を保持する目的か?
Standby with RTC 非動作 非動作 非動作 非動作 LSI/LSE 非動作 動作 LCD表示を停止してもよければ、
このモードも使える
Stanby without RTC 非動作 非動作 非動作 非動作 LSI/LSE 非動作 非動作 電源OFFのスイッチを付けて
制御したほうが楽では?
人が簡単にON/OFF出来ない用途で
使用するのか?

 詳しくは、下記の文献を参照すると良いと思います。

 3.2.7.2 STOPモードの使用
 今回のGPSロガーでは、STOPモードを選択することにしました。
 上の表にあるように、STANDBYモードと違いRTCだけでなくLCDの表示が保持できる点が魅力です。
 電池切れか、まだ復帰して動作可能か否かが判別できます。
 STOPモードと通常モードは下記の様に切り替わります。

STOPモードへの
突入条件
通常モードへの
復帰条件
コメント
WKUPスイッチがONされた時 WKUPスイッチがONされた時 突入条件と復帰条件は互いに独立し、
横並びで対となっていません
(突入条件と復帰条件は独立事象です)

WKUPスイッチで突入して、
ロガーがゆすられると復帰します

机の上に置きっぱなしすると、
Gセンサー信号が変化しないので突入し、
RTCタイマーのアラームで一定時間後に復帰し、
そのままであれば間欠的にログを繰り返して
電池が切れるまで継続します
Gセンサーが静止条件と判断し
それが一定時間経過したとき
STOPモードがあらかじめ指定された
時間だけ経過した場合
(案(未実装)) GPSデータが一定時間
継続して捕捉できない場合
GセンサーのMOT信号があらかじめ設定した
値以上となり信号がCPUに出力された場合
(案(未実装)) バッテリ-電圧が低下して
間欠モードでロガーを動作させたい時
.

 3.2.7.3 FreeRTOS上で制御の流れ
 FreeRTOSの制御化でどのようにSTOPモードに突入し、復帰させたかを下記に解説します。
 基本は非常にシンプルです。
 STOPモードに入る時の条件が、Wake-up後は通常動作に何も関係せず単なるパワーオンリセットと同じ動作となります

  1. 状況を判断して、STOPモードへの突入条件を判別する
  2. 突入条件が揃ったら、STOPモードへの準備の諸条件を設定する
  3. STOPモードを実行
  4. Wake-up信号でPower On RESETと同じシーケンスで何事もなかったようにFreeRTOSを実行する

 突入条件判断は、下記の項目を既にソフトウェアとして実装しています。

TASK 突入条件とその判断 実行しているソフトウェア
vADC_Task()

ADCタスク
Gセンサーから計算した静止Gが、
1.05G以下もしくは0.95G以上の範囲で
静止状態が5分以上継続したと時

Gの上下限、時間判定は変更可能
500mS毎に下記関数が呼ばれる

static void Check_G_stationary( void ){
uint32_t d;

 d = (uint32_t)(data_acceleration * 100);
 if ( ( d > G_STAY_MAX ) || ( d < G_STAY_MIN ) ){
    /* Keep stationary 5 min then go to STOP mode */
    g_stationary_time = G_TIM_5MIN;
  } else {
    if ( --g_stationary_time == 0 ){
       /* Task(5_SwPwr) will make "STOP" mode */
       flg.BIT.f_wakup = 1;
    }
  }
}

条件が整った時に、
  flg.BIT.f_wakup = 1;
となる
vSwitch_and_Pwr()

スイッチ状態チェックタスク
WKUPスイッチが押されたら無条件で
条件成立
vSwitch_and_Pwr()が10mS毎に周期的に呼び出され、
下記の処理が行われる

  /* Go to STOP mode by WKUP Switch */
  if ( STM32_WKUPSW_GetState() != Bit_RESET ){
    if (j){
       --j;
    } else {
       Standby_and_wakeup();
    }
 } else {
       j = 20;
 }

Standby_and_wakeup()がSTOPモードの前処理
Switch_and_Pwr()

スイッチ状態チェックタスク
flg.BIT.f_wakup = 1が成立した際の
処理
   /* Go to STOP mode by G sensor stationary condition */
  if ( flg.BIT.f_wakup == 1){
     Standby_and_wakeup();
  }

Standby_and_wakeup()がSTOPモードの前処理

 Standby_and_wakeup()では、下記のような処理をしています。

処理 ソースコード 解説
各タスクの終了  /* Send termination message */
 xQueueSendToBack( sdcardQueue, "END", portMAX_DELAY );
#if (USE_LCD == 1)
 xQueueSendToBack( LcdQueue, "END", portMAX_DELAY );
#endif
 vTaskDelay( 500 / portTICK_RATE_MS );
 while ( rec_status ){ /* Wait for file close */
    vTaskDelay( 20 / portTICK_RATE_MS );
 }
 /* Delete all tasks except me */
#if SET_MON
 vTaskDelete( xHandle_vMon_Task );
#endif
 vTaskDelete( xHandle_vGPS_Task );
 vTaskDelete( xHandle_vADC_Task );
#if (USE_LCD == 1)
 vTaskDelete( xHandle_vLCD_Task );
#endif
 vTaskDelete( xHandle_vFile_Task );
 vTaskDelete( xHandle_vTim0_Task );
先ずはファイル書込み動作を止めさせて、
ファイルをCloseさせます

LCDにSTANDBYの表示をするように
LCD表示タスクにキューを発行

処理が間に合う様に待ってから、
各タスクを削除します
Wake-upするための
RTCアラームAの設定
  /* Disable the alarm A */
 RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
 /* Set alarm time */
 RTC_AlarmStructInit( &RTC_AlarmStructure );
 RTC_AlarmStructure.RTC_AlarmTime = RTC_TimeStructure;
 h = STP_DURATION_TIME / 60;
 m = STP_DURATION_TIME % 60;
 RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes += m;
 if ( RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes >= 60 ){
   RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes -= 60;
   h++;
   if ( h >= 24 ){ h = 23;} /* If overflow, set upper limit */
   RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours += h;
   if ( RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours >= 24 ){
     RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours -= 24;
   }
 }
 RTC_AlarmStructure.RTC_AlarmMask =    RTC_AlarmMask_DateWeekDay;
 RTC_SetAlarm( RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmStructure );
アラームAに起動時間を設定します
define設定されたSTP_DURATION_TIME(分単位で)
で休んでいる時間を決定

時間は24時間以内に設定可能
(休む時間を24時間以上とする場合には、日付け変更まで含め対応する必要がある)

今回は"Programmable alrm"を使用
したが、他に
”Periodic auto-wakeup"
をする方法を選べば1秒から36時間
までの時間設定ができそうである
(2012/9/22)
IOポートを
アナログ入力に
(最少漏れ電流に
なるように)
 /* Port set for standby */
 RCC_AHBPeriphClockCmd(
 RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB |
 RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD |
 RCC_AHBPeriph_GPIOE | RCC_AHBPeriph_GPIOH,
 ENABLE
 );
 /* IO re-initialize (LCD & SWD ports are kept) */
 /* PA.1,2,3,x,x,6,7,8,9,10,x,x,x,x,15 for LCD (Not use SWD(PA13,PA14)) */
 GPIO_InitStructure.GPIO_Pin =
     GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12 | \
     GPIO_Pin_13 | GPIO_Pin_15;
 /* Set analog mode above pins */
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_400KHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
 GPIO_Init( GPIOA, &GPIO_InitStructure );
 以下省略
 /* Set analog mode above pins */
 GPIO_Init( GPIOE, &GPIO_InitStructure );
IOポートをLCD駆動を除き
アナログ入力設定

ポートが最少リーク電流になるように
設定

これが最善か未結論
パワーラインは
再度しっかり
OFF出来るように
 /* GPS & SD-CARD Power Control again */
 SD_PWR_Init();
 SD_PWR_Off(); /* Keep Off */
 GPS_PWR_Init();
 GPS_PWR_Off(); /* Keep Off */
GPSとSDカードの電源電圧を測定したところ
少し電圧が残っていた為、先のような処理とした
WKUPスイッチの
イベント起動設定
(STOPモードからの
復帰)
 /* Set wake-up Switch for restart */
 STM32_WkupSWInit();
 STM32_SetEXTI();
WKUP条件の設定
割込み処理設定
STOPモードへの
エントリ
(Standbyモード
も可、但しLCDは
動作せず)
#if 1
 /* Enter low power condition */
 PWR_UltraLowPowerCmd( DISABLE );
 FLASH_SLEEPPowerDownCmd( DISABLE );
 /* works well with LCD is on */
 PWR_EnterSTOPMode(
   PWR_Regulator_LowPower,PWR_STOPEntry_WFI );
#else
 /* works well but LCD is off */
 PWR_EnterSTANDBYMode();
#endif
STOPモードへのエントリ

 STOPモードから通常モードへの復帰は下記の様になっている。

処理 ソースコード 解説
WKUPスイッチからの
復帰
/*
* Wake-up process from WAKEUP switch to go to RESET condition
*/
void EXTI0_IRQHandler ( void ){
  SYSCFG_RIDeInit();
  NVIC_SystemReset();
}
割込みが発生したら
System RESET
を強制的に起こさせて
FreeRTOSが何事もなかったように
起動される

ある面では、過去は何も覚えていない
RTCアラームAからの
復帰
/*
* Wake-up process from RTC alarm-A to go to RESET condition
*/
void RTC_Alarm_IRQHandler(void){
  SYSCFG_RIDeInit();
  NVIC_SystemReset();
}
Gセンサーからの
信号に基づく復帰
 /* Set wake-up by G-sensor MOT input */
 STM32_WkupMOTInit();
 STM32_SetEXTI6();
STM32_WkupMOTInit()は、PE6の端子を
外部割込み端子として設定し、Gセンサーの
FF端子の変化を検出するための初期設定

STM32_SetEXTI6()は、STOPモード時にFF
信号の立ち上がりを検出して通常モードに
復帰する
(2012/9/22)

 結果としては、STOPモード移行と復帰が上手く出来るようになったが、未だ良く解っていない点が多い。
 もう少し、ロジックを明確にして納得できる処理に仕上げたいと思う
 GセンサーによってSTOPモードからの復帰が出来るようになったことで、目標とする動作モードが全て完成した(2012/9/22)。
 
 消費電流はまだ4mA前後で、それも安定しているわけではない。
 消費電流の残りがどんな部分から成り立っているのかを調査することも残務としては重要と思っている。

 <2012/9/15記>
 3.2.7.4 RESETの種類とSTOPモードの関係
 現時点で認識している不具合として、
  ”STOPモードから復帰した際に、RTCの付属機能であるBackupレジスタ群の内容がリセットされてしまっている”
 が挙げられる。
 まだ原因究明に至ってないが、今までの検討結果をメモしておく。
 原因が初期化プロセスのBUGと判明(2012/9/22)。
 
 少し遠回りとなるが、RESETの理解が最初に必要となる。
 REFERENCE MANUALS CD00240193.PDF (Doc ID 15965 Rev 6 July 2012) p85
 を見ると、リセットは大別すると下記の様になる。 

リセットの
種類
リセット要因 解説 RTC及びBackup
レジスタの状態
System Reset ①NRST端子に外部からLowレベル信号が
 入った場合と下記②から⑦の要因で
NRST端子はCPU内で下記②から⑦の要因がOR接続され、20μS以上のLowレベル信号を発生する
またNRST端子は、外部要因でのリセットをワイヤードORで接続できる機能を持つ
(CD00240193.PDF page 86 Figure 11参照)
システムリセットが
発生しても、
それだけでは
影響せず
②WWDG(Window watchdog)のカウント終了時 ウォッチドッグタイマーでのリセット
③IWDG(Independent watchdog)のカウント終了時
④ソフトウェアリセット ソフトウェアの実行として意図的に
システムリセットを実行できる
⑤Low-Power マネジメントリセット スタンバイモード及びストップモードへの
突入時に必要に応じて、モード突入せずに
システムリセットが発生するように設定出来る
⑥オプションバイトローダーリセット 詳細分析出来ていません
⑦スタンバイモードからの復帰時 スタンバイモードからの復帰はシステムリセットを伴う
Power Reset ①パワーオンリセット及びパワーダウンリセット
 (POR/PDR)
電源が不安定な状況では内容が
保持されていないと判断してリセットする
影響あり
下記RTC関連リセット
②相当
②ブラウンアウトリセット(BOR)
RTC &
Backup register
Reset
①ソフトウェアでの意図的リセット ソフトウェアの実行として意図的に
システムリセットを実行できる
意図的なリセット
②パワー関連リセット
 (上記Power Reset内①及び②の発生時)
. 影響あり
初期化される

 RTC及びバックアップレジスタに注目すると、

 と言うことになります。
 しかしながら、現時点ではSTOPモードからの復帰でシステムリセット内④ソフトウェアリセットを実行していますが、RTCとバックアップレジスタの内容をうまく使えていません。
 問題は、①初期化が起きている可能性と、②ソフトウェアで意図しない初期化を実行している(Bug)可能性とが考えられますが、現時点では原因がはっきりしていません。(2012/9/22)
 
 <2012/9/22記>

 やはり予想通りBUGでした。
 RTCの値及びバックアップレジスタ(RAM)の値は、STOPモードからの復帰ではリセットされず、内容が保持されていました。
 パワーオンリセット(STOPモードからの復帰にも利用)に常にバックアップレジスタ(RAM)をリセットしていました。
 これで、バックアップレジスタ(RAM)を有効に使うことが出来るようになりました。





3.3 <個別対応/問題点>
TOP02_001.PNG - 934BYTES

3.3.1. ファイルネーム生成
<ファイルネーム>
 ファイルネームをダブらずに作ることは結構難しく、今までPICなどのデータロガーで使っていたRTCの時刻からの作成が上手くいかないことで、紆余曲折を繰り返しました。
 残念なことにSTM32L152のRTCはRESETによって、時刻、日付けのレジスタがゼロリセットされてしまうことがわかりました。
 従って、パワーオンリセット後にRTCの値を読み込んでファイルネームにしても同じ名前になってしまい上書きされてしまう不具合が多発しました。
 そこで乱数を使ってファイルネームにしたが、ダブりの頻度は減少したが相変わらず同じ名前になる現象が無くなりませんでした。
 最終的には、STM32Lの持つFLASH DATA MEMORY内にシーケンシャル番号を記憶してファイル名にしましたが、これで安定するか否かは今後の評価です
 その後のチェックで問題なく動作しているので、この方式で確定しました(2012/9/2)。
 心配点は毎回更新しますので、書込み回数が増えてデータ不良を起こす可能性が増加します。
 本来なら、多数決を取ったり、複数のエリアをうまく配分して対応すべきですがアマチュア的な使い方ではすぐに問題とはならないでしょう。


 
追加情報(2012/9/15)
 
DATASHEET CD00277537.PDF (Doc ID 17659 Rev 6 January 2012) page69のTable32に、Flash Memoryの書換え可能サイクル(Ncyc)が規定されているが、
 EEPROM data memoryとして300,000回となっている。
 STOPモードや電源ON/OFFの度にファイル名が更新されることを考えて、1日最大50回書換えが行われたと仮定して
   300000/50 = 6000(日) → 6000/365 = 16.4(年)
 は、同じセルを使い続けてもセルが壊れないで使えることになる。
 先ずは素人の使い方としては問題ないレベルでしょう。
 ちなみに、DEBUGの為にソフトウェア更新を繰り返していますが、プログラム領域の書換え回数は同じ表で1万回と規定されています。
 1日、50回書き換えて週末2日間をフルにDEBUGしていても、
   10000/50 = 200(日) → 200/(52x2) = 1.9(年)
 だけ1個のCPUで遊べることになります。
 いくら頑張っても、これだけDEBUGを集中して行う気力はないですが。

処理 処理内容 解説
FLASH内の
シーケンシャル数字の
更新
main.c

/* Storage data in EEPROM */
#define USER_EEPROM_BASE ((uint32_t)0x08080000)
#define USER_FILE_NAME ((uint32_t *)USER_EEPROM_BASE)

int main( void ){
 /* 省略 下記は初期化の一部 */
 /* Data read from EEPROM */
 file_name_base = *USER_FILE_NAME;
 DATA_EEPROM_Unlock();/* Unlock Data EEPROM memory */
 if ( DATA_EEPROM_ProgramWord( (uint32_t)USER_FILE_NAME, ++file_name_base )
   != FLASH_COMPLETE )
 {
   file_name_base = 0;
 }
 DATA_EEPROM_Lock();
 /* 以下省略 */
32ビットデータを
Flash Data memoryから
読出し、+1した上で
再度か書き込む

もし書込みが上手くいかない
場合にはデータをゼロとする
ファイル名への変換 main.c

void vFile_Task( void *pvParameters ){
 /* 省略 下記は初期化の一部 */
 t = get_fattime(); /* Create file name */
 if ( file_name_base ){
  t = file_name_base; /* File name from EEPROM data */
 }
 redirect_to_buff_out( name_buf );
 xprintf( "G%07x.txt", t );
 redirect_to_buff_zero_out();
 res = f_open( &File1, (const TCHAR*)name_buf, FA_CREATE_ALWAYS | FA_WRITE);
 /* 以下省略 */
ファイル名を決定する際に
先ずget_file()で時刻情報を
持ってくる

次にFLASHから読出した
数字がゼロでなければ
そちらを使う

<ファイルのタイムスタンプ>
 ファイル名でのダブりが上記方法で無くなったので、固定値を入れました。
 スタンバイ動作も動き始めてRTCの値が保持できるようになったので、初期値に戻ることが少なくなったことも良い方向です(2012/9/2)。





3.3.2. IO初期化が難しい!
 STM32(Lだけでなく)は、IOと機能モジュールの端子共有の仕方、各モジュール単位での細かな電力マネージメント、更に動的な変更のし易さ等、良いところが多くあります。
 その為に、初期化行為が慣れないとうまく出来ません。
 Lチカ一つするだけでも、マニュアルを読んで一から始めると大変なことになります。
 
 LibとしてのSTM32L1xx_StdPeriph_Driver\src\stm32l1xx_gpio.cには、下記のようなコメントがあります。
 私の追加コメントも参照してください。

説明記述 コメント
1. Enable the GPIO AHB clock using
 RCC_AHBPeriphClockCmd()
先ずは、モジュールにクロックを送る必要があります
マイクロプロセッサは同期回路の塊と考えるべきで、同期の基本はクロック供給です
クロックの供給を始めるとCMOS回路は多かれ少なかれ貫通電流や充放電電流が流れ、
クロック周波数が比例して電流が増えます
この資料は、これらのことが簡潔に述べられています
STM32Lは、省電力の見地から各機能ブロックのクロックを停止し、必要なモジュールのみクロックを供給し使用します
クロックを供給しないと、その後の設定が上手くレジスタに保持されません

クロックの分配はIOによって違い、①AHB、②APB2、③APB1によって違い、ここも的確な関数を呼び出すことを難しくしています
この資料(CD00277537.pdf January 2012 Doc ID 17659 Rev 6 page12 )のFig1 Ultralow power STM32L15xxx block diagramを良くチェックしてください
2. Configure the GPIO pin(s) using GPIO_Init()
  Four possible configuration are available for each pin:
   - Input: Floating, Pull-up, Pull-down.
   - Output:
      Push-Pull (Pull-up, Pull-down or no Pull)
     Open Drain (Pull-up, Pull-down or no Pull).
     In output mode, the speed is configurable:
      Very Low, Low, Medium or High.
   - Alternate Function:
     Push-Pull (Pull-up, Pull-down or no Pull)
    Open Drain (Pull-up, Pull-down or no Pull).
   - Analog:
    required mode when a pin is to be used as ADC channel,
    DAC output or comparator input.
GPIOとしての設定です(一部、他の機能へ設定する際にも重要です)
STM32Lでは、各ピン毎に細かな設定が出来る点は使い易い点でしょう
<入力>
   フローティング、プルアップ、プルダウンが選択可能
   (使用しない端子は、出力にする方法もありますがSTM32Lではアナログ設定で
    フローティングが良さそうです)
<出力>
   CMOSのコンプリメンタリ出力とするかオープンドレインとするか
   追加で、プルアップ、プルダウンそして何も付けない選択が出来ます
   出力の同期回路は、そのクロック周波数を選択できる(スピードで消費電力が変わるので注意)
<違う機能(AF Alternate Function)で使用>
   AFでは、次の3も一緒に設定が必要です
   AFが出力となる場合には、GPIOの出力回路が使われるので、この設定がAFであっても有効
<アナログ入力>

  アナログは、未使用に端子にも省電力の為に有効のようです
3- Peripherals alternate function:
   - For ADC, DAC and comparators, configure
     the desired pin in analog mode using
     GPIO_InitStruct->GPIO_Mode =GPIO_Mode_AN
   - For other peripherals (TIM, USART...):
     - Connect the pin to the desired peripherals' Alternate
      Function (AF) using GPIO_PinAFConfig() function
     - Configure the desired pin in alternate function mode using
      GPIO_InitStruct->GPIO_Mode = GPIO_Mode_AF
     - Select the type, pull-up/pull-down and output speed via
      GPIO_PuPd, GPIO_OType and GPIO_Speed members
     - Call GPIO_Init() function
AFとしての機能設定
 ADC、DACとコンパレータとして使用する時は、アナログモードとする
 その他の機能(タイマー、UART、SPI、I2Cなど)
   先ずどの機能をどのポートに接続するかを決定(GPIO_PinAFConfig())
   上記2でGPIO_Init()でポートの機能設定(時に出力ポート時)
4. To get the level of a pin configured
  in input mode use GPIO_ReadInputDataBit()
ポートの端子電圧をディジタル値としてHi/Lo呼ぶ時には、
GPIO_ReadInputDataBit()を使います
5. To set/reset the level of a pin configured in output mode use
  GPIO_SetBits()/GPIO_ResetBits()
ポートへの出力は、read modified writeの時間的なリスクがない、
GPIO_SetBits()(Hiを出力)/GPIO_ResetBits()(Loを出力)を使用する
但し、ポートの初期設定が出力になっていなければ、意味を持たない
6. During and just after reset, the alternate functions are not
  active and the GPIO pins are configured in input floating mode
  (except JTAG pins).
リセット直後は、ポートは入力モードでプルアップダウンがないフローティング状態となる
一般にCMOS入力回路をフローティング状態で接続しておくと貫通電流が増えるが
アナログ入力端子と共有することから考えると、(推測ながら)ディジタル入力は読み込む動作に
同期して信号入力され、その他の時にはCMOSの貫通電流防止回路が入っていると思われる
7. The LSE oscillator pins OSC32_IN and OSC32_OUT can be used as
  general-purpose (PC14 and PC15, respectively) when the LSE
  oscillator is off. The LSE has priority over the GPIO function.
PC14、PC15(LSE)、PH0、PH1(HSE)をXtal発振以外で使う際には、注意が必要
先ずは発振回路としての起動が優先され、その後ソフトウェアでの機能設定が行われる
8. The HSE oscillator pins OSC_IN/OSC_OUT can be used as
  general-purpose PH0 and PH1, respectively, when the HSE
  scillator is off. The HSE has priority over the GPIO function.

 設定が難しい点は、下記のようなことで一週間以上悩んだことでも判ります。
 これは、SPIのSCK、MISO、MOSIの機能をPortDに割付ける為の初期化の一部です。

間違ったコード 何が問題?
/* Configure the SPI each pin */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
/* SPI SCK pin configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_PinSource1;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* SPI MISO pin configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_PinSource3;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* SPI MOSI pin configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_PinSource4;
GPIO_Init(GPIOD, &GPIO_InitStructure);
左のソースコード内の赤字が問題でした

GPIO_PinSourceX → GPIO_Pin_X
にしなければなりません

定義が下記の様にことなります
#define GPIO_PinSource1  ((uint8_t)0x01)
#define GPIO_Pin_1   ((uint16_t)0x0002)

GPIO_PinSource1は、
GPIO_PinAFConfig( GPIOD, GPIO_PinSource1, GPIO_AF_SPI2 )
の様に、AFのコンフィグで使用します
IOコンフィグの時点で使用する時には使えません!





3.3.3. Monitor機能

 Monitorプログラムは、ここでも紹介しています
 原型は、ChaNさんのアイデアをいただいています。
 例えば、ChaNさんのサンプル・プロジェクトで
     \ffsample\lpc17xx\main.c
 のmain()を見ると、アイデアが良く解ると思います。

 今回のモニタープログラムは、下記のような機能を持っています。

 <重要な注意点(2012/9/9)>
 
コマンドの実行は、各タスクが処理しているハードウェア資源に直接アクセスしてPCへの情報を作成します。
 排他的な処理を行っていません!
 
これは、monitorプログラムがDebugの目的で、タスク開発の途中で使用すること前提としているからです。
 もしも定常的に使用するのであれば、Semaphoeなどを使用してリソースマネジメントをしっかり実行する必要があります。
 特にファイル関係のコマンドをタスクと並行して実施すると動作がフリーズするなど致命的な不具合が発生します。

 課題は、USBの仮想COM機能でも同じ動作をさせることです。
 以前、ダブルリングバッファを使って、LPC1768で動作確認していますが、移植作業は優先順位が今のところ低いです。





3.3.4. I/Oのredirect
 ChaNさんソフトウェアパッケージを入手するために、FatFs関連のWebページに行くと、
 サンプル・プロジェクト
 で、
 ffsample\lpc17xx\xprintf.c, xprintf.h
 を入手できる。
 その中を覗くと下記のような部分が出てくる。

ファイル名 重要なポイント
xprintf.h #define xdev_in(func) xfunc_in = (unsigned char(*)(void))(func)
extern unsigned char (*xfunc_in)(void);
int xgets (char* buff, int len);
int xfgets (unsigned char (*func)(void), char* buff, int len);
int xatoi (char** str, long* res);
xprintf.c void (*xfunc_out)(unsigned char);  /* Pointer to the output stream */
static char *outptr;

/* Put a character */
void xputc (char c){
 if (_CR_CRLF && c == '\n') xputc('\r'); /* CR -> CRLF */
 if (outptr) {
  *outptr++ = (unsigned char)c;
  return;
 }
 if (xfunc_out) xfunc_out((unsigned char)c);
}

/* Put a null-terminated string */
void xputs (        /* Put a string to the default device */
const char* str     /* Pointer to the string */
){
 while (*str){
  xputc(*str++);
 }
}

void xfputs (          /* Put a string to the specified device */
void(*func)(unsigned char), /* Pointer to the output function */
const char* str        /* Pointer to the string */
){
 void (*pf)(unsigned char);

 pf = xfunc_out;      /* Save current output device */
 xfunc_out = func;    /* Switch output to specified device */
 while (*str){        /* Put the string */
  xputc(*str++);
 }
 xfunc_out = pf;      /* Restore output device */
}

 この機能を使うと、xprintfで出力先の1文字出力先をRedirectすることが出来る。
 同様に何らかのデバイスからの一文字入力もRedirect出来る。
 
 この機能は非常に便利なのだが、FreeRTOS上でこのRedirectを使うことを考えると少し工夫がいる。
 タスクがどこのタイミングでスイッチングされるかは不明なので、xprintf機能が各タスクから同時に多重に呼ばれて処理することを想定する必要がある。
 そこで、
  ① 今、どのタスクから呼ばれているかを認識すること
  ② 各タスク毎にRedirect先を管理する
 ことが必須となる。

 ①に関しては、FreeRTOSの機能の中で、
 #define INCLUDE_pcTaskGetTaskName
 を1とすることで、TCB内にあるタクス名をpcTaskGetTaskName()関数で読み出すことが出来ようになる。

 ②に関しては、Redirectする入出力関数テーブルをタスク別に置くことで解決できる。

 これを、下記の様にソフトウェア作成して対応した。

処理 処理内容
タスク名の1文字目だけを
簡略的に認識する
(タスク名の先頭は同じにしない)

更にRedirect使用を予定するタスクは
数字から始める

これで数字をテーブル参照の
インデックスとすることが可能
main.c内でのタスク生成でタスク名称の先頭一文字を数字に

xTaskCreate( prvLCDTask,(const signed char * )"0_LCD",
mainLCD_TASK_STACK_SIZE,(void *)NULL, mainLCD_TASK_PRIORITY, NULL );
xTaskCreate( vMon_Task,(const signed char *)"1_Mon",
16*14,(void *)NULL,(tskIDLE_PRIORITY + 1), NULL );

xTaskCreate( vGPS_Task,(const signed char *)"2_GPS",
16*8,(void *)NULL,(tskIDLE_PRIORITY + 1), NULL );

xTaskCreate( vFile_Task,(const signed char *)"3_FILE",
16*10,(void *)NULL,(tskIDLE_PRIORITY + 1), NULL );

xTaskCreate( vTim_Task0,(const signed char *)"Tim0",
configMINIMAL_STACK_SIZE,(void *)NULL,(tskIDLE_PRIORITY + 1), NULL );

xTaskCreate( vADC_Task,(const signed char *)"4_ADC",
16*8,(void *)NULL,(tskIDLE_PRIORITY + 1), NULL );
タスク別にRedirectの
関数テーブルを管理する
printf-ChaN.c内で

* xprintf redirect function table */
struct xPRNTREDIRECT{
  /* if not zero, xprintf outputs to ram buffer */
  char *buff_ptr;
  /* Output function pointer */
  void (*xfunc_out)(unsigned char);
  /* Input function pointer */
  unsigned char (*xfunc_in)(void);
};
関数管理デーブルの構造体

/* For some reason lint wants this as two separate definitions. */
typedef struct xPRNTREDIRECT xPrintRedirect;
xPrintRedirect redirect_inf[NO_REDIRECT_TASK];
/*上記の配列へのインデックスはタスク名の先頭数字で管理*/

NO_REDIRECT_TASK
は、xprintf-ChaN.h内で5が定義されている
使用例 その1 monitor.c内のmon()タスクでパソコンとのシリアル通信をRedirectする例が
下記の様になる

  /* Open Uart to communicate with Host PC */
  if ( redirect_to_getc(getc_uart) == 0 ){
    while (1){ /* No actgion foever */}
  }
  if ( redirect_to_putc(putc_uart) == 0 ){
    while (1){ /* No actgion foever */}
  }

上記で、入力がgetc_uart()に、出力がputc_uar()に接続される
使用例 その2 main.c内のvGPS_Task()タスク内で

  xGpsPortInitMinimal( mainCOM_GPS_BAUD_RATE );
  if ( redirect_to_getc(getc_gps) == 0 ){while (1){/* No action foever */}}
  if (redirect_to_putc(putc_gps) == 0){while (1){/* No action foever */}}
使用例 その3 main.c内のvFile_Task()タスク内では、redirect_to_null()を使って、
redirect_to_buff_out( name_buf );
の様にRAM内のバッファへRedirectしている
動作例 Redirectの実際 printf-ChaN.c内

/* Redirect to null device */
int redirect_to_null(void){
 /* NullへのRedirect */
}

/* Redirect to output ram buffer */
int redirect_to_buff_out(char *ptr){
 /* RAM内バッファへのRedirect */
}

/* Redirect to specific input function */
int redirect_to_getc(unsigned char (*xfunc_in)(void)){
 /* 入力関数へのRedirect */
}

int redirect_to_putc(void (*func_out)(unsigned char)){
 /* 出力関数へのRedirect */
}

char xgetc(void){
unsigned char i;

  i = *pcTaskGetTaskName(NULL); /* Check task name */
  i -= '0';
  if (redirect_inf[i].xfunc_in){
    return redirect_inf[i].xfunc_in(); /* ここが入力関数へのRedirect */
  } else {
    return 0;
  }
}

void xputc (char c){
unsigned char i;

  if (c == '\n'){
    xputc('\r'); /* CR -> CRLF */
  }
  i = *pcTaskGetTaskName(NULL); /* Check task name */
  i -= '0';
  if (redirect_inf[i].buff_ptr){
    *redirect_inf[i].buff_ptr++ = (unsigned char)c;
    return;
  }
  if (redirect_inf[i].xfunc_out){ /* ここが出力関数へのRedirect */
    redirect_inf[i].xfunc_out((unsigned char)c);
  }
}

 尚、ChaNさんのprintfはシンプルですがパワフルなので愛用させていただいてます。
 上記Redirect機能でどのタスクでも使えるようになりました。
 ChaNさん作成であることを強調する意味で私は、
 printf-ChaN.c printf-ChaN.h
 とrenameして使っています。





3.3.5. Ring Buffer
 ここを参照ください。
 リングバッファの機能は、RAM領域があれば活用したい機能です。
 ADCデータの平均化でも使用したいのですが、CPUリソースとの兼ね合いとなります。
 従来のプログラムを修正したのは、下記の点です。

コード(ring.c) コメント
uint16_t ring_putc (ring_t *ring, uint8_t c){
  if (ring_is_full(ring)){
    return 1;
  }
  taskENTER_CRITICAL();
  ring->buff[ring->tail++] = c;
  if (ring->tail == ring->size){
    ring->tail = 0;
  }
  taskEXIT_CRITICAL();
  return 0;
}
タスク切り替えの禁止区間を設けた

従来から、この問題は潜在していたはずだが
ユーザーインターフェイスとしては低速なので
発生頻度が低いことで実質の問題が発生して
いなかったと思われる
uint16_t ring_getc (ring_t *ring){
  if (ring_is_empty(ring)){
    return 1;
  }
  taskENTER_CRITICAL();
  ring->dt_got = ring->buff[ring->head++];
  if (ring->head == ring->size){
    ring->head = 0;
  }
  if (ring->head == ring->tail) { /* Initialize ring */
    ring->head = ring->tail = 0;
  }
  taskEXIT_CRITICAL();
  return 0;
}





3.3.6. LibraryのBUG
 GPSデータからの時刻データに基づきRTCを設定するソフトウェアを作成中に、妙な動きをしたのでDebugしてみたところ原因が判明した。

症状 出典 修正
曜日を初期化しないと2012年と設定しても曜日の数値が年度に影響しておかしくなった

出典
\STM32L_Discovery_Firmware_Pack_V1.0.2
\Libraries\STM32L1xx_StdPeriph_Driver\inc\stm21l1xx_rtc.h

入手場所
STM32L-DISCOVERY
Discovery kit for the STM32L EnergyLite 32-bit MCUs
"Design support" STM32L-DISCOVERY firmware package V.1.02
typedef struct{
  uint32_t RTC_WeekDay;
  uint32_t RTC_Month;
  uint8_t RTC_Date;
  uint8_t RTC_Year;
}RTC_DateTypeDef;
typedef struct{
  uint8_t RTC_WeekDay;
  uint8_t RTC_Month;
  uint8_t RTC_Date;
  uint8_t RTC_Year;
}RTC_DateTypeDef;





3.4 <その他
TOP02_001.PNG - 934BYTES

 3.4.1.直感的に違和感がありませんか?

 ポートのHi/Loを制御するのに、
    GPIO_ResetBits(GPIOD, GPIO_Pin_0);
 を使うのも一つの方法ですが、そのサブルーチン内(stm32l1xx_gpio.c)を覗くと、
    GPIOD->BSRRH = GPIO_Pin_0; /* ポートPD0=0とLoにする */
 となっているだけです。
 サブルーチンコールしなくても対応できる範囲です。
 そこで直接呼び出して使い始めたら、Hi/Loが逆転してしまいました。
 例えば、上記はRESET作業です。
 何故、BSRRHLoとなるのでしょうか?
    GPIOD->BSRRH = GPIO_Pin_1;  /* Port Lo */
    GPIOD->BSRRL = GPIO_Pin_1;  /* Port Hi  */
 上記を見ると逆に見えませんか?

 実は、これはHとLの意味がポートのはHi/Loの状態と結び付いていないことを前提にしなければいけません。
 BSRRの32ビットレジスタを16ビットの2つのレジスタに分けて、アドレスマップの上位と下位に分けたHとLです。

stm32l1xx.h内で下記の様にレジスタが宣言されています

typedef struct{
  __IO uint32_t MODER;
  __IO uint16_t OTYPER;
  uint16_t RESERVED0;
  __IO uint32_t OSPEEDR;
  __IO uint32_t PUPDR;
  __IO uint16_t IDR;
  uint16_t RESERVED1;
  __IO uint16_t ODR;
  uint16_t RESERVED2;
  __IO uint16_t BSRRL; /* BSRR register is split to 2 * 16-bit fields BSRRL */
  __IO uint16_t BSRRH; /* BSRR register is split to 2 * 16-bit fields BSRRH */
  __IO uint32_t LCKR;
  __IO uint32_t AFR[2];
} GPIO_TypeDef;

 STM32の開発エンジニアが、もう少しセンスがあってアドレスを逆に配置すれば誤解が薄れたと思うのは私だけでしょうか?
 ビットのセットとリセットですから、他に矛盾が生じないと思うのは私がまだ充分CPU全体を把握してないからでしょうか?

 <2012/9/15記>
 3.4.2.Debugがいつもうまくいくとは限らない!

 プログラム開発で混沌とした状況に陥ると、ターゲットに向き合う時間を長くするより、一度気分転換するように心がけています。
 自分を冷静に置くことで、思わぬアイデアや問題点が見えてくることがあります。
 昔から、Debggerを駆使した開発は殆ど経験がなく、プログラムをFLASHに書いて動作状況を確認し推測しながらDebugするスタイルが基本です。
 とは言っても、OpenOCDでDebugが出来る環境は本当に困った時には大変有効なツールとなります。
 しかし、困った時に立ち上げて使おうとすると、そんなタイミングに限ってうまく動作しないことが多くあります。

 ここで、過去の問題を対処療法で片付ける方法を紹介しています
 ねむいさんのぶろぐでも、書込みさえ出来ない状況に陥ることを、最近の"元に戻せなくなった"との質問に答える形で紹介しています。
  (7月28日のOpenOCDの新MPSSEドライバを試すの下段(最終行から探したほうが早いくらい)でのコメント)
 今回、公開したサンプルプログラムもそのままではOpenOCDでのDebugが出来ない点をご理解ください。
 例えば、下記の部分を修正する必要があります。

ファイル名 該当場所 回避方法 コメント
main.c /*
 * Idle hook
 */
void vApplicationIdleHook( void ){
#if configENTER_LOWPWR == 1
  /*
  Called on each iteration of the idle task.
  In this case the idle task just enters a low(ish) power mode.
  */
  PWR_EnterSleepMode( PWR_Regulator_ON, PWR_SLEEPEntry_WFI );
#endif
}
PWR_EnterSleepModeを
コメントアウトする
スリープモードに入ると
JTAG/SWDとの交信が
途絶える
stdby_wkup.c void Standby_and_wakeup( void ){
 中途省略
 /* IO re-initialize (LCD & SWD ports are kept) */
#if 0
 /* PA.1,2,3,x,x,6,7,8,9,10,x,x,13,14,15 for LCD & SWD */
 GPIO_InitStructure.GPIO_Pin =
 GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12;
#else
 /* PA.1,2,3,x,x,6,7,8,9,10,x,x,x,x,15 for LCD (Not use SWD(PA13,PA14)) */
 GPIO_InitStructure.GPIO_Pin =
 GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12 | \
 GPIO_Pin_13 | GPIO_Pin_15;
#endif
 以下省略
SWDポートをアナログ
入力に設定しない
#if文を1とする





4 サンプルプログラム
IMAGE12.GIF - 2,801BYTES
TOP02_001.PNG - 934BYTES

内容 ZIPファイルへのリンク 注意点
STM32L152
GPS Logger Software
8月末目標で最終Debug中
スタンバイ制御も含め、もう少し公開を遅らせます(2012/9/2)

まとめの作業を実行しています
公開目標2012年9月16日(2012/9/9)


STM32L-Olimex_P152_FreeRTOS_wrk1.zip
(2012/9/14コンパイル)

    
<参考として当面残します>
下記の不具合と未実装を確認しています
(充分なDebugが出来ていませんので、下記以外でも不具合が起きる場合が予想されます
自己責任でお試しください)
1)SDカードはFAT32でフォーマットされた物を使用してください
 PCに装着してファイルをカードからCut&pastした後に、再フォーマットせずに使用すると
 書込みが上手くいかない不具合が発生します
2)電源電流をモニターしていると、大電流が一瞬(100mAのアナログ電流計で
 80mAくらい振れる場合あり)流れます。原因がはっきりしていません
3)STOPモードから復帰した際に、RTCの付属機能であるBackupレジスタ群の
 内容がリセットされてしまっている(ここで分析しているが、まだ原因不明のまま
4)加速度センサーによるSTOPモードからの復帰プログラムが未実装です
5)モニター機能で、排他処理を実装していません

仕様
1)Gセンサーで基板の揺れがなく、静止状態との判定が15分継続した場合に
 STOPモードに移行します。
2)STOPモードからの復帰は、
 ①45分何もしたいと再起動します
 ②WKUPスイッチを押すと再起動します
3)STOPモードに入っている際は、LCD上に”STANDBY"と表示されます
4)USERキーを操作すると、LCD画面表示内容が切り替わります
 キー操作をしない場合には、一定時間毎に表示内容が変わります
5)RS232インターフェイスでPC画面上でTera termのような端末ソフトで
 メモリ内容等の動作状況をモニターすることが出来ます
 通信速度は、115200baudです
6)STOPモードだけでなく、全ての状態でLED表示はOFFとしています
 (main.c内#define LED_ON 0 を 1とすると制御可)
2012年9月22日アップデート
wrk1を使用せず、
こちらをダウンロードしてください

STM32L-Olimex_P152_FreeRTOS_wrk2.zip
(2012/9/16コンパイル)

wrk2のファイル構成はこのようにしました

ROM/RAM使用量
エリア サイズ 使用量 比率
ROM 128KB 119.3KB 93%
RAM 16KB 5.2KB
+8KB(heap)
=13.3KB
83%
**不具合**
下記の不具合と未実装を確認しています
(充分なDebugが出来ていませんので、下記以外でも不具合が起きる場合が
予想されます。自己責任でお試しください)

<未解決>
1)SDカードはFAT32でフォーマットされた物を使用してください
PCに装着してファイルをカードからCut&pastした後に、再フォーマットせずに
使用すると書込みが上手くいかない不具合が発生します

5)モニター機能で、排他処理を実装していません

6)消費電流
 ①STOPモード時の電流が4mA以下になりません
  何が要因なのか未解明

 ②スパイク的な大電流発生の要因が未解明

<解決済>
2)電源電流をモニターしていると、大電流が一瞬(100mAのアナログ電流計で
80mAくらい振れる場合あり)流れます。原因がはっきりしていません
→原因が不明のまま、現象が消えました
 ハードの問題だったのか、ソフトなのかも不明です

3)STOPモードから復帰した際に、RTCの付属機能であるBackupレジスタ群の
内容が上手く読みだせるようになりました
ここで分析している

4)加速度センサーによるSTOPモードからの復帰プログラム実装出来ました


**仕様**
1)Gセンサーで基板の揺れがなく、静止状態との判定がX分継続した場合に
STOPモードに移行します。
G_SEN_STATIONARY_JUDGE_TIME (main.h)

2)STOPモードからの復帰は、
 ①Y分何もしたいと再起動します
   STP_DURATION_TIME (main.h)
 ②WKUPスイッチを押すと再起動します
 ③基板(Gセンサー)か揺らされた場合

3)STOPモードに入っている際は、LCD上に”STANDBY"と表示されます
”ERROR"と表示の場合もあります(SDカード制御エラー時)
復帰時に”RESTART"(RAMがバックアップされなかった時)もしくは
”SURVIVE"と表示(電源OFFなく、バックアップ情報が保持できた時)


4)USERキーを操作すると、LCD画面表示内容が切り替わります
キー操作をしない場合には、一定時間毎に表示内容が変わります

5)RS232インターフェイスでPC画面上でTera termのような端末ソフトで
メモリ内容等の動作状況をモニターすることが出来ます
通信速度は、115200baudです
起動から一分以内にPCからの操作がないとRS232ラインをOFFして、
電源電流を削減します


6)STOPモードだけでなく、全ての状態でLED表示はOFFとしています

7)基板上の半固定VRでLCDのコントラストが調整出来る

wrk1からwrk2への変更は、これにまとめました




TOP02_001.PNG - 934BYTES









































































































2012/9/2=135, 9/9=212,9/15=282,9/23=385