« 2009年9月 | トップページ | 2009年11月 »

2009年10月の記事

開発環境をWindows7に移行

Windosw 7が発売されました。私もしっかりと(?)、先行予約で購入してしまい、週末はせっせとVistaから環境を移行中です。7の由来は諸説ありますが、Windows 7番目のメジャーリリースといわれています。自分が買った歴代Windowsを並べてみると、Windows 3.0→ 95→ 98 → Me → XP → Vista → Windows 7となり、自分的にも7代目のWindowsです。      
95~Meはマイナーリリースですので、5世代お付き合いしたことになりますが、われながらマイクロソフトさんのいいカモだと思います。新しいのが出ると、ついつい、、

Windows 7の使い勝手やインプレッションは他の記事に譲るとして、Arduino, STM32 Primer2開発環境の移行を行いましたので結果を以下に示します。

Arduio IDE

Arduino 0017を使っていますが、問題なく動作。まあ、IDEはJavaアプリですから動いて当たり前です。FT2232のドライバーは最新の2.04.16をFTDIからダウンロードして使用しました。Windows 7対応の記載はありませんが、インストール、動作とも問題はありませんでした。

STM32 Primer2 (Ride7)

Vista時代に使っていた、CD_STM32-Primer_BN28.zipをインストール。こちらはRLinkドライバのインストールに問題がありました。インストーラーの処理では、RLinkインストールの際にOSバージョンチェックでエラーとなり、RLinkドライバーのインストールがスキップされます。

[インストールDir]\Ride\Driver\RLinkDrvにドライバーファイルが展開されていますので、インストーラーから抜けた後に、RLinkUSBInstall.exeをXP互換モードで実行することでドライバーがインストールできました。デバッグ、書き込みなど一通り動かしてみましたが、今のところ問題はありません。XP/Vistaから変わった点として、Windows 7のスタートメニューにRide7で最近使ったプロジェクトが表示されるため、1アクションで直近のプロジェクを開くことができます。

Win7_StartMenu

ちょっと便利ですが、この開き方ででは、プロジェクト内で最後に使っていたソースコードを自動的に開いてくれない点が残念です。(Windowsの問題ではなく、Ride7の動作仕様だと思います)

その他

今回購入したのはProfessionalエディションの32bit(DSP)版です。64bit版はまだ時期尚早かと思い見送ったのですが、やっぱり64bit版にチャレンジしたほうが面白かったと後悔中です。パッケージ版なら32bit/64bitの両メディアが入っているので、64bitでだめなら32bitに代えるということもできたのですね、、。パッケージ版にこのようなメリトッがあることを認識していなかったです。最近は電子工作関連の情報収集に時間を取られて、このあたりの事前チェックが不足していました(言い訳ですが)。

Windows 7はVistaに比べてレスポンスが改善されたと巷で言われていますが、確かにVistaで感じたモッサリ感がなくなり改善が実感できます。

あと、Windows 7の新機能ではありませんが、Windows Live Writerなるブログ編集ソフトがあることを初めて知りました。Windows 7にはメールクライアントが添付されておらず、Windows Liveからのダウンロードになるのですが、その際に発見。この記事はLive Writerを使って入力しています。これまでは、ブラウザーの編集画面経由で記事の入力を行っており、編集機能が限られていたのですが、なかなか便利です。ソースコードを整形して貼り付けプラグインもあり今度使ってみようと思います。フォントの指定ができるようになったため、デフォルトのMSゴシックでなく、メイリオを指定してみました。

2009/11/3追記:
Writerでフォント形式を指定すると、投稿後ブラウザーから編集を行うとフォーマットが崩れてしまう問題があったため、メイリオフォントの指定はスタイルシートの編集に変更しました。
ソース整形の「Insert Code for Windows Live Writer」はスタイルシートを記事の中に埋め込む形式のせいか、フォント指定と同様に、投稿後のブラウザー編集に問題がありました。

上記の機能はココログとは相性が悪いようです。

STM32 Primer2のバッテリー交換

前回バッテリーが不調と書きましたが、原因はどうも以下のようです。

Primer2のKnown Issue(既知問題)に「USBケーブルの抜き差しを頻繁に行うとバッテリーが死んでしまう」が上がっています。USBケーブルを挿入するとその都度充電シーケンスが開始されるため、これを短時間に繰り返すとバッテリーが死んでしまうというものです。

実はデバック中に過充電を気にして、充電完了状態になる毎にUSBケーブルを抜き差したため、もろに上記の状態を作ってしまいました。ForumのFAQ(IMPORTANT: How to preserve the STM32 Primer batteries)に「Primerの充電回路は単純であり、バッテリーの保護のために長時間USBケーブルを接続したままにしてはいけない」とあったため、デバック中にUSBケーブルの抜き差しを行っていました。

実は、上記の注意書きは、NiMHバッテリーを使ったPrimer1の話なんですね。単にPrimerとしか書いてないため、Primer2に対する注意書きだと勘違いしていました。よく考えてみると、過充電で発火の危険を伴うLi-Ionバッテリーを使った製品に「簡易充電回路」が認められるわけがなく、Primer2も当然制御ICを使った充電回路を持っています。

バッテリーの交換

ということで、バッテリーを交換しました。バッテリーは860mAhのリチウムイオンポリマー電池をスイッチサイエンスさんから購入。製品添付のバッテリーは容量が400mAhと小さめで、もともと交換を考えていたため、まあよいかというところです。

購入したバッテリーはサイズ的にはぴったりで、元のバッテリーを外して基盤に両面テープで固定しました。

Primer2_Battery_Change

まだ、2回程充電した程度ですが、今のところ問題なく動いています。充電電流は60mA程度で充電回路に負担がかかっているという感じはありません。バッテリー稼働時間はLCDをONにしたままでも余裕で6時間はいけそうでいい感じです。当然充電時間も延びましたが。

2009/10/25追記:
STM32 Primer2の電源回路とバッテリー駆動時間」で行った測定方法を使用して、12時間動作させてもバッテリーは3.77Vでした。まだ数時間は動きそうですので、十分な駆動時間となりました。

Li-Ionバッテリーの放電下限電圧

過去のブログ記事で、バッテリーのデーターシート上では放電下限電圧が2.75Vなのになぜ3.5Vでshutdownしてしまうのか、3Vあたりまでバッテリーを使えないのかと書きました。携帯電話のバッテリーが1セル・800mAh程度で今回購入したバッテリーと同程度のスペックですので、携帯電話の動作がどうなっているかを調べてみました。結果は以下のとおりです。

バッテリーアラームが出て操作を受け付けなくなるまで放電した状態で電池を取り出し、電圧を測ると3.5Vでした。Primer2のshutdown電圧と同じです。かみさんの携帯で試しても同様でした(どちらも同じキャリアですが、端末メーカーは異なります)。ということは、Li-Ion電池は実質的に3.5V/cellあたりまでしか使ってはいけないということでしょうか。理由としては、以下が考えられそうです。

このWebページにあるLi-Ion電池の放電特性を見ると、3.5Vあたりまではだらだらと電圧が低下しますが、3.5Vから急激に電圧が低下することがわかります。放電電流が少ないほど3.5V境界での落ち込みが激しくなっています。Primer2も放電電流が少ない使い方になるため、電池を3.0Vあたりまで使うとshutdownタイミングの判断が難しく、下手をすると危険領域の2.75Vまであっという間に放電してしまうリスクがありそうです。加えて、3.5Vまで放電した時点で容量の90%以上を使っており、これ以上放電させても稼働時間延長への寄与は少ないと思われます。

この点を考えて3.5Vで打ち止め(shotdown)しているのだと理解した次第です。

STM32 Primer2のバッテリー不調

Primer2のバッテリー駆動時間が異常に短くなってしまいました。ひょっとしてバッテリーがお亡くなりになったかかも。いけない使い方はしていないつもりなのですが、、

充電回路の動作を確認する意味も込めて、バッテリー充放電時の電圧・電流を計ってみました。

リチウムイオンバッテリーの充電(予備知識)

先ずはリチウムイオンバッテリーの基礎知識をこのサイトでお勉強です。リチウムイオンバッテリーの充電は、以下のフェーズに分かれます。

  1. プリチャージ(バッテリー電圧が低い場合、電流を少し流して電圧が回復するかを確認 →バッテリーの正常性確認)
  2. 定電流充電(定電流で急速充電を行う →90%程度まで充電)
  3. 定電圧充電(セル電圧が4.2Vに到達すると、定電圧で充電電流が一定の値以下になるまで充電)

充電時の電圧変化

電圧変化をテスターの写真を並べて示します。実はこのテスター20年以上前の代物なのですが(よく動いている)、測定値はCircleOSのUTIL_GetBat()関数で得られるバッテリー電圧と合っていますので概ね正確だと思います。今度新しいテスターを買わねば。UTIL_GetBat()関数は、バッテリー電圧をSTM32 MCUのADCで測定します。

1)電圧低下による自動shutdown直後の電圧

Shudonw直前は3.4V台まで電圧が落ちるが、電源が切れると電圧が少し復活する。
01_v_aftershutdown

2) 充電中の電圧

4.19V(=4.2V)となっています。「STM32 Primer2の電源回路とバッテリー駆動時間」で書きましたが、STM32 Primer2はバッテリー電圧3.5Vでshutdownしてしまうためか、定電流充電フェーズがなく、いきなり定電圧充電が始まっています。
02_v_charging

3) 充電が完了すると、電圧が4.1Vになります

03_v_chargecomp

4) バッテリー駆動状態にする

いきなり電圧が3.7V以下になります。電池の内部抵抗の関係?すぐに本体の電源をOFFすると4V台の電圧が見えますが、電源ONのままにするとあっという間に3.5Vに低下していきます。電圧落ちが早すぎるような。
04_v_poweron_aftercharge_2

電流の測定

1) 72MHz動作時の電流

36MHz動作では、40.3mAでした。36MHzで、バックライト輝度を最低まで落とすと34.4mAとなりました。ATMega 328 16MhzのArduinoで25mA程度ですので、LCDがあることを加味するとまぁまぁの低消費電力性能か。
A_72mhz_blmid

2)充電電流

4.2Vの定電圧充電時の電流(放電時に対して電流の流れが逆になるため、マイナス表示になっています)。だんだん、電流が減っていきます。
A_chargestart

3)充電完了時の電流

僅かに放電しています。
A_chargeend

まとめ

バッテリーが不調のため、正しい充放電特性になっていない可能性がありますが、以下のことが言えます

1) STM32 Primer2では放電終止電圧が3.5Vと高めのため、充電はいきなり定電圧充電から始まる。ひょっとしてバッテリーを深く放電させると、定電流充電が走り、ボルテージレギュレーター充電回路への負荷増になるため定電流充電が動かないようにしている?充電回路の抵抗値(Rprog)で充電電流を制御でき、STM32 Primer2では68KΩになっているため、充電制御IC(ST社L6924D)のデーターシートの式を使って計算すると171mAになります。本体を動かしながら充電すると200mA以上の電流供給が必要になりますが、この電流が取れない?こちらはUSBバスパワーからの直送のため容量的には足りる筈です。

2) 定電圧状態の充電電流は徐々に減少して、10mAあたりで充電が終了しました。充電終了電流も充電回路の抵抗で制御ができ、STM32 Primer2では13mAとなります。従って、充電終了の制御もちゃんと行われています

この先どうするか

バッテリーの電圧があっという間に下がってしまうのは、現象としてメモリー効果が起きているみたいですが、リチウムイオン電池ですからそれはないはずです。やっぱり電池が死んだのでしょうか。

この際なので、容量の大きいバッテリーに交換を行います。スイッチサイエンスさんに860mAhのリチウムイオンポリマーバッテリーを注文しました。STM32 Primer2のForumを見ると、バッテリー容量を上げても充電回路は対応できる(充電時間が延びるだけ)とのスレッドがあったので、バッテリー交換で問題が解消するかを確認予定です。

STM32 Primer2でシリアルポートを使用する(割り込み編)

前回に引き続き、STM32 Primer2でシリアルポート(USART2)を使った実験を行いました。前回はポーリング形式のデーター送受信でしたが、今回は割り込みを使ってみました。

検証環境

  • CircleOS  3.80
  • STM32F10x Standard Peripherals Library V3.1.0 (以下STライブラリ)
  • Ride7 V7.24.09
  • シリアルポートとしてUSART2を使用

動作概要

  1. データー受信時(RXNE = 1)に割り込みをかけ、受信データーを受信バッファ(RxBuffer)に格納
  2. getchar()関数の呼び出しで受信バッファから1文字分のデーターを取得。受信データーがない場合は、0を返す(標準のgetcharと異なる部分)
  3. putchar()関数の呼び出しで送信データーを送信バッファ(TxBuffer)に格納
  4. 送信バッファのデーターを、Transmit Data Register Empty(TXE)割り込発生契機にUSART2_DRに書き込むことによってデーターを送信する。送信バッファが空になると割り込みを停止する

一文字毎に割り込みを発生させるのもレジスタの退避などでオーバーヘッドがありそうですが、前回書いた通り、STM32 Primer2のCircleOSではユーザーアプリに処理が回ってくる間隔が長いため(33ms以上)、データーの取りこぼしを防止するためには受信側の割り込み処理は必須です。

STM32における割り込みの優先度

Cortex-M3 テクニカルリファレンスマニュアルとSTライブラリの間で、割り込み優先度レベルの表記に差分がありました。内容を以下に示します。

STM32は16レベルの割り込み優先度をサポートします(4bit幅の優先度レベルフィールド)。優先度レベルフィールドは、横取り優先度(preemption priority)とサブ優先度(sub-priority)に分割されます。また、横取り優先度とサブ優先度の分割はPriority Groupの指定によってプログラマブルになっています。

Cortex-M3 テクニカルリファレンスマニュアルでは、Priority Group 0は横取り優先度に優先度レベルフィールドの上位7 bitを割り当てる定義になっているため、STM32では16レベル全てが異なる横取り優先度になります。

一方で、STライブラリではmisc.hにて以下のようにNVIC_PriorityGroup_x定数を定義しています。 NVIC_PriorityGroup_0に対してサブ優先度フィールドを4 bit割り当てているため、横取り優先度がありません。

NVIC_PriorityGroup PreemptionPri SubPriority Description
NVIC_PriorityGroup_0    0 0-15  0 bits pre-emption priority
4 bits subpriority
NVIC_PriorityGroup_1 0-1 0-7 1 bits pre-emption priority
3 bits subpriority
NVIC_PriorityGroup_2 0-3 0-3 2 bits pre-emption priority
2 bits subpriority
NVIC_PriorityGroup_3 0-7 0-1 3bits pre-emption priority
1bits subpriority
NVIC_PriorityGroup_4 0-15 0 4bits pre-emption priority
0bits subpriority

実は、STライブラリのmisc.hの中でPriority Group値の読替を行っており、STライブラリのNVIC_PriorityGroup_0は、Cortex-M3 テクニカルリファレンスのPriorityGroup 7に該当し等価です(当たり前ですが)。
 #define NVIC_PriorityGroup_0         ((uint32_t)0x700)

また、上位2 bitが横取り優先度となるグループ設定を行った場合、Cortex-M3 テクニカルリファレンスの表記では、横取り優先度レベルは8 bitフィールドの上位2 bitで表されるため、0xC0, 0x80, 0x40, 0x00の値を取ります。一方STライブラリでは3, 2, 1, 0の値を指定します。

STM32 Primer2の場合、CircleOSにてNVIC_PriorityGroup_2を設定しています。また、Mems用のTIM2割り込みを、横取り優先度=1, サブ優先度=1に設定してしています。今回、USART2の割り込に対して、横取り優先度=2, サブ優先度=0を設定しました。

プログラムコード

前回の「STM32 Primer2でシリアルポートを使用する」で使用したSTM32F10X_IO_putchar.cに割り込みハンドラとバッファ制御処理を追加しました。割り込み優先度はSTライブラリを使用して以下のように設定しました。
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

Priority GroupはCircleOS起動時に設定済みのため触っていません。割り込みハンドラはCircleOSが管理しているため、以下のようにCircleOSのUTIL_SetIrqHandlerL関数を呼び出して登録します。
 UTIL_SetIrqHandler(0x00D8, USART2_IRQHandler);

0x00D8はUSART2_IRQ用割り込みベクターのアドレス(ベクターテーブル先頭からのオフセット値)です。

割り込みハンドラー(USART2_IRQHandler)では、STライブラリのUSART_GetITStatus()関数を使って割り込み要因を調べ、要因別の処理を行っています。コード全体を以下に示します。

#include "stm32f10x.h"
#include "circle_api.h"

/* Private defines -----------------------------------------------------------*/
#define BUFF_SIZE     255

/* Private variables ---------------------------------------------------------*/
unsigned char __io_init_done = 0;

u8 TxBuffer[BUFF_SIZE];
u8 RxBuffer[BUFF_SIZE];

vu8 TxWrCounter = 0x00;         // Write counter for Tx data buffer
vu8 TxRdCounter = 0x00;         // Read counter for Tx data buffer
vu8 RxWrCounter = 0x00;         // Write counter for Rx data buffer
vu8 RxRdCounter = 0x00;         // Read counter for Rx data buffer

//unsigned int __io_Main_Osc = 8000000;

/* Private function prototypes -----------------------------------------------*/
void USART2_IRQHandler(void);


//called by the user app to tell us what is the main osc frequency
void __io_SetMainOscFreq( unsigned int NewFreq )
    {
    //__io_Main_Osc=NewFreq; //save new freq value
    __io_init_done = 0; //force reinit at next putchar
    //we need to keep this even if we ignore the NewFreq param
    //because if the user calls this, it means that he probably changed his clock settings
    //so we must really reinit
    }

/*******************************************************************************
* Function Name  : InitUSART
* Description    : Initialize USART2 and NVIC
*                  Set Irqvector of USART2 IRQ
*
*******************************************************************************/
void __io_init( void )
  {
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Enable GPIOx and AFIO clocks */
  /* USART2 used for Primer2 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

  /* Enable USART2 clocks */ 
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  /* Set IRQ Handler to NVIC table */
  UTIL_SetIrqHandler(0x00D8, USART2_IRQHandler);

  /* NVIC_Configuration to enable USART2 IRQ */
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
  /* Configure the GPIO ports */
  /* Configure USARTx_Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Configure USARTx_Rx as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

/* USARTx configuration ------------------------------------------------------*/
  USART_InitStructure.USART_BaudRate = 9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No ;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  /*
  USART_InitStructure.USART_Clock = USART_Clock_Disable;
  USART_InitStructure.USART_CPOL = USART_CPOL_Low;
  USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;
  USART_InitStructure.USART_LastBit = USART_LastBit_Disable;
  */
 
  /* Configure the USARTx */
  USART_Init(USART2, &USART_InitStructure);

  USART_ClearITPendingBit(USART2, USART_IT_RXNE);
  USART_ClearITPendingBit(USART2, USART_IT_TXE);

  /* Enable USART2 Receive and Transmit interrupts */
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
  USART_ITConfig(USART2, USART_IT_TXE, ENABLE);

  /* Enable the USART2 */
  USART_Cmd(USART2, ENABLE);

  // IO init done!
  __io_init_done = 1;
  }


/*******************************************************************************
* Function Name  : DeenitUSART
* Description    : Restore Irqvector of USART2 irq
*                  Disable USART2 and USART2 irq
*
*******************************************************************************/
void DeinitUSART(void)
{
  USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
  USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
  USART_Cmd(USART2, DISABLE);

  __io_init_done = 0;
}


/*******************************************************************************
* Function Name  : __io_putchar
* Description    : Write data to TxBuffer (Do not write USART DR)
*                  Actrual data transfer is done by IRQ Handler
* Input          : charactor to send
*******************************************************************************/
void __io_putchar( char c )
   {
   //init UART if needed
   if( !__io_init_done )
      {
      __io_init();
      }

   // \n is not enough. Need \r too!
   if( c == 0x0A )
      {
      __io_putchar( 0x0D );
      }

/*
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
      {
      }
    USART_SendData(USART2, (u8)c);
*/
    u16 emptyFlag;

    if (TxWrCounter == TxRdCounter)
        emptyFlag = 1;
    else
        emptyFlag = 0;
    TxBuffer[TxWrCounter] = (u8)c;
    if (++TxWrCounter == BUFF_SIZE)
        TxWrCounter = 0;

    if (emptyFlag)
      USART_ITConfig(USART2, USART_IT_TXE, ENABLE);
   }

int
putchar (c)
int c;
    {
    __io_putchar( (char) c );
    return c;
    }


/*******************************************************************************
* Function Name  : __io_getchar
* Description    : Read data from RxBuffer (Do not read USART DR)
*                  Actrual data reception is done by IRQ Handler
* Return         : RxBuffer not empty-> Received charactor
*                : RxBuffer empty    -> 0
*******************************************************************************/
int __io_getchar()
   {
   //init UART if needed
   if( !__io_init_done )
      {
      __io_init();
      }

/*
   unsigned short value;
   unsigned short wStatus;

   while(USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET)
      {
      }
   value = USART_ReceiveData(USART2);

   return (int)(value);
*/
    u8 ch, rxRdNext;

    if (RxRdCounter == RxWrCounter)
        return 0;

    rxRdNext = RxRdCounter + 1;
    if (rxRdNext == BUFF_SIZE)
        rxRdNext = 0;

    ch = RxBuffer[rxRdNext];
    RxRdCounter = rxRdNext;
    return (int)ch;
   }


int getchar()
   {
   return __io_getchar();
   }


/*******************************************************************************
* Function Name  : USART2_IRQHandler
* Description    : Write received data to RxBuffer
*                  Send data from TxBuffer
*******************************************************************************/
void USART2_IRQHandler(void)
{
  u8 rxWrNext;

  if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  {
    rxWrNext = RxWrCounter + 1;
    if(rxWrNext == BUFF_SIZE)
    {
      rxWrNext = 0;
    }
    if (rxWrNext == RxRdCounter)     // Rx buffer full (Write counter overtake Read counter)
    {
      USART_ReceiveData(USART2);
      return;
    }
   
    RxBuffer[rxWrNext] = USART_ReceiveData(USART2);
    RxWrCounter = rxWrNext;          // Now RxWrCounte point next index (Wr > Rd)
  }

  if(USART_GetITStatus(USART2, USART_IT_TXE) != RESET)
  {   
    if (TxRdCounter == TxWrCounter)         // Tx buffer empty
    {
      USART_ITConfig(USART2, USART_IT_TXE, DISABLE);
      return;
    }
   
    USART_SendData(USART2, TxBuffer[TxRdCounter++]);
    if(TxRdCounter == BUFF_SIZE)
    {
      TxRdCounter = 0;
    }
  }
}

ビルド方法

STM32 Primer2でシリアルポートを使用する」に示した方法を使ってRide7でビルドします。STライブラリのソースに加えて、STM32F10X_IO_putchar.cもプロジェクトフォルダーにおいてコンパイルします。LD Linker設定のUART0 Putcharライブラリ設定はNoにします。

アプリケーションからの使用方法

以下のようにgatchar(), putchar()関数を呼び出すと、裏で割り込みベースの入出力が動きます。

/*******************************************************************************
* Function Name  : Application_Handler
* Description    : Management of the Circle_App.
*
* Input          : None
* Return         : MENU_CONTINUE
*******************************************************************************/
enum MENU_code Application_Handler(void)
    {

    // TODO: Write your application handling here.
    int c;
   
    while ( (c = getchar()) !=0 )
        putchar(c);

    // This routine will get called repeatedly by CircleOS, until we
    // return MENU_LEAVE

#if 1
    // If the button is pressed, the application is exited
    if(BUTTON_GetState() == BUTTON_PUSHED)
        {
        BUTTON_WaitForRelease();
        DeinitUSART();
        return MENU_Quit();
        }
#endif

    return MENU_CONTINUE;   // Returning MENU_LEAVE will quit to CircleOS
    }

アプリケーションを終了する際に、DeinitUSART()を呼んで割り込みの停止とUSART2の停止を行っています。割り込みを有効にしたままアプリケーションを終了した後で、シリアルポートにデーターを送るとCircleOSがメニュー操作を受け付けなくなってしまったため、後始末として入れています。

リングバッファの管理が手抜きなのですが(書き込みポインターが読み出しポインターに追いついたか否かだかで制御しており、バッファーに溜まっているデーター量は管理していない)、取りこぼし等発生することなく動いています。

mbedプロトタイピングツール

ARMのホームページを覗いたらこんなのを見つけました。ちょっと物欲がうずきます。

  • ARMとNXP、マイクロコントローラ搭載システムのプロトタイピングを高速化するmbedを発表
  • NXP LPC1768(Cortex M3)を使ったプロトタイピング用のモジュールと開発環境がmbed.orgで公開されています
  • モジュールは以下のように40pin DIP構成となっており、ブレッドボードで使うのに最適(Arduino Nanoみたいです)。

Mbed

開発環境はmbed.orgがクラウド上で提供。各自のPC上にコンパイラ・IDEなどの開発環境を構築する必要がないというのが今風です。曰く「新規ユーザーが60秒でHello Worldを動かせる」とか。

Digital I/O, PWM, SPIなどのペリフェラルドライバーがC++形式のライブラリとして提供されています。また、2行LCDへの表示・Servoといったよく使う機能もC++オブジェクト形式のライブラリとして提供されています。C++でペリフェラルやI/O制御をラッピングするコンセプトはArduinoと同じですね。C++を組み込み系で使うのは、パフォーマンスやメモリー使用効率の点では最適とは言えないのかもしれませんが、複雑な低レベルI/O制御を隠蔽してお手軽に使えるという点は、Arduinoでそのメリットを実感しています。

水色のピンは全てDigital I/Oとしても使用可能で、入出力の数に不足はなさそう。気になる点は、JTAGを使ったデバッグが未サポートと思われる点です(webページをざっと見ると、デバッグにはprintfを使えと書いてあります)。

今なら、先行予約で$60。う~ん、USBを使ったJTAGデバッグがこのお値段で実現できれば買いですが。

NXPのようなMCUチップベンダーがこの手のプロトタイピングツールや開発環境に投資(協賛?)するのは、NXP製品へのファンを増やすためでしょうか。STM32 Primer2も同様ですが、この手のプロトタイピングツールで組み込み製品の開発ができるとは思えず、入門者(学生さんなど)がARM系MCUになじんで、開発者の裾野を広げる(将来的に量産の組み込み製品に採用してくれる)ことを期待しているのかなぁと思います。

STM32 Primer2でシリアルポートを使用する

GPSモジュールなど外部装置との通信を行うために、シリアルポートの動作試験を行いました。Ride7のライブラリフォルダーを覗いていると、printfやputs関数でシリアルポートに出力が出来そうに思えます。「楽勝」と思って動かしてみるとなかなかはまってしまいました。以下、シリアルポートと格闘した結果分かったことです。

2009/10/02追記:
9/30に公開した手順はRide7のDebug機能を使ってプログラムをSTM32 Primer2に書き込むことはできますが、Circle_Mgrを使った転送を行うとリンカーエラーが発生することが分かったため対策を追記しました。検証不足のまま記事を公開してしまいました。注意せねば。。

検証環境

  • 執筆時点で最新のRide7 V7.24.09とRkit-ARM V1.22.09を使用
    → Rkit-ARM V1.22.09のST Standard Peripherals Libraryが曲者でした
  • 試験プログラムはCircleOSアプリとして作成
  • シリアルポートは拡張コネクタに信号が出ているUSART2を使用
    → USART1はIrDA用です

Ride7開発環境のライブラリ構成

STマイクロ社が、STM32のUSART, GPIO, TIMERなどのペリフェラルにアクセスするためのST Standard Peripherals Library(以下STライブラリ)と呼ばれるドライバーモジュールを提供しています。この提供形態に2種類あり、旧バージョン(V2.0.3)のSTライブラリをコンパイル済み形式で提供していることに加え、最新のSTライブラリ(V3.1.0)をソースのみで提供しています。

[Ride7インストールディレクトリ]\Ride\lib配下には以下のライブラリが存在します;

  • lib\ARM:
    コンパイル済みの旧STライブラリ、コンパイル済みの低レベルI/O関数(STM32x_IO_Putchar_thumb.a)、コンパイル済みsmall_printf(Smallprintf_thumb.a)
  • lib\ARM\include:
    旧ライブラリのヘッダーファイル
  • lib\ARM\STM32F10x_Lib:
    旧ライブラリのソースファイル、example、ドキュメント
  • lib\ARM\STM32F10x_Lib_V3.0.1:
    新ライブラリのソース、ヘッダ、example、ドキュメント(フォルダ名はV3.0.1ですが、格納ファイルのバージョンはV3.1.0です)
  • lib\ARM\io_putchar:
    USARTへの入出力を行うための、低レベル(MCUタイプ依存)I/O関数のソース。printf, puts, putchar, getcharからこの関数を呼び出すことでUSART経由の送受信を行っている
  • lib\ARM\small_printf
    GNU C標準ライブラリのprintf関数はサイズが大きいため、機能を制限したコンパクト版のソース。Small no-float printfが浮動小数点の書式付き表示を割愛した最もコンパクトなライブラリです。

ライブラリのリンク条件の設定方法

1)STライブラリ

Ride7プロジェクトのデフォルト設定では、コンパイル済みの旧ライブラリがリンクされる点が注意点です。Project→Propeties →LD Linker →Libraries → Use OLD Precomplied Library が、デフォルトでYesになっています。キャプチャー画面を以下に示します。

Ride7_ld_menu

後で手順を説明しますが、STM32のペリフェラルにアクセスする場合は、STライブラリV3のソースをプロジェクトフォルダーにコピーしてライブラリを個々にコンパイル・リンクすることが推奨されています。(Use OLD Precomplied LibraryをNoに設定する)。

STライブラリは、使用するMCUの種別(I/Oピン数、メモリ容量)、クロック周波数など、STM32 Primer2のハード構成に対応するために、一部のヘッダファイルをカスタマイズする必要があります。コンパイル済みの旧ライブラリは、STM32 Primer2に対応した変更を加えずにビルドしているためUSART関連は正常に動作しません。そのため、STライブラリV3のソースを修正して再コンパイルする手順を採用しました。

2)io_putchar

こちらはSTライブラリのリンク条件(コンパイル済みか否か)に関わらず、デフォルトはコンパイル済みのライブラリをリンクします。LD Linker設定メニューでUSART0 PutcharをYesに設定することでio_putcharをリンクし、USART経由の入出力を有効にします。

<stdio.h>をincludeするとputchar, getcharがUSARTにアクセスしないため注意が必要です。(恐らくstdin, stdoutを使用する関数をリンクしてしまう)

Circle_Mgrを使ってプログラムを書き込む場合、ソースをプロジェクトフォルダーにコピーしてコンパイルする必要があります。コンパイル済みのライブラリは使用できません。詳細手順は後に示します。

3)small_printf

LD Linkerのprintf capabilitiesメニューから指定が可能です。io_putchar同様に、Circle_Mgrを使ってプログラムを書き込む場合、ソースをプロジェクトフォルダーにコピーしてコンパイルする必要があります。

USART2を使用するためにカスタマイズが必要なライブラリファイル

1)lib\ARM\io_putchar\STM32F10X_IO_putchar.c

デフォルトでは、USART1を使用する形でハードコーディングしてあります。このままでは、ライブラリをリンクしても正常に出力してくれません。

そのため、USART2を使用するようにソースを修正してライブラリの再ビルドを行う必要があります。Ride7に「STM32x_IO_Putchar.rprj」を読み込んでソースを変更後ビルドを行うと、STM32x_thumbフォルダ配下にSTM32x_IO_Putchar_thumb.aファイルが生成されます。このファイルをlib\ARMにコピーします。

ソースファイルの変更点は以下です。stm32circle.comのForumでも修正イメージが複数掲載されています。

void __io_init( void )
  {
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;

  /* Enable GPIOx and AFIO clocks */
  /* USART2 used for Primer2 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);  // GPIOAのクロック共有をON

  /* Enable USART2 clocks */ 
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // USART2はAPB1配下(GPIOAはAPB2配下のため分けて設定要)

  /* Configure the GPIO ports */
  /* Configure USARTx_Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;    // Txピン番号を指定
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);   // 出力モードの設定

  /* Configure USARTx_Rx as input floating */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;   // Rxピン番号を指定
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);  // 入力モードの設定

  USART_InitStructure.USART_BaudRate = 9600;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No ;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
  /* Configure the USARTx */
  USART_Init(USART2, &USART_InitStructure); // BaudRate等のパラメーターを設定
  /* Enable the USART2 */
  USART_Cmd(USART2, ENABLE);
  // IO init done!
  __io_init_done = 1;
  }

__io_putchar(), __io_getchar()関数内でもUSAR1を指定しているため、USART2に修正を行います。

2) lib\ARM\STM32F10x_Lib_V3.0.1\Libraries\CMSIS\Core\CM3\stm32f10x.h

□MCUタイプをMD(Medium density)からHD(High density)に変更。

#if !defined (STM32F10X_LD) && !defined (STM32F10X_MD) && !defined (STM32F10X_HD) && !defined (STM32F10X_CL)
  /* #define STM32F10X_LD */   /*!< STM32F10X_LD: STM32 Low density devices */
  /* #define STM32F10X_MD */   /*!< STM32F10X_MD: STM32 Medium density devices */
  #define STM32F10X_HD         /*!< STM32F10X_HD: STM32 High density devices */
  //#define STM32F10X_CL   /*!< STM32F10X_CL: STM32 Connectivity line devices */
#endif

□USE_STDPERIPH_DRIVERを有効にする(コメント解除)

#if !defined  USE_STDPERIPH_DRIVER
/**
* @brief Comment the line below if you will not use the peripherals drivers.
   In this case, these drivers will not be included and the application code will
   be based on direct access to peripherals registers
   */
  #define USE_STDPERIPH_DRIVER
#endif

□HSE(高速外部クロック)の周波数を12MHzに変更(デフォルトは8Mhz)

#if !defined  HSE_Value
#ifdef STM32F10X_CL   
  #define HSE_Value    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
#else
  #define HSE_Value    ((uint32_t)12000000) /*!< Value of the External oscillator in Hz */
#endif /* STM32F10X_CL */
#endif /* HSE_Value */

このクロック周波数を変更しないと、BaudRateの設定(分周比の設定)が正しく行われないため、送受信で文字化けが発生します。この問題を見つけるまで時間を要しました。

3) lib\ARM\STM32F10x_Lib_V3.0.1\Libraries\CMSIS\Core\CM3\system_stm32f10x.c

以下のシステムクロック種別を修正。
#define SYSCLK_FREQ_HSE    HSE_Value
/* #define SYSCLK_FREQ_24MHz  24000000 */
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
//#define SYSCLK_FREQ_72MHz  72000000

4) lib\ARM\STM32F10x_Lib_V3.0.1\Project\Template\stm32f10x_conf.h

今回使用するペリフェラルドライバ関連のヘッダファイルをコメント解除。
/* Uncomment the line below to enable peripheral header file inclusion */
/* #include "stm32f10x_adc.h" */
/* #include "stm32f10x_bkp.h" */
/* #include "stm32f10x_can.h" */
/* #include "stm32f10x_crc.h" */
/* #include "stm32f10x_dac.h" */
/* #include "stm32f10x_dbgmcu.h" */
/* #include "stm32f10x_dma.h" */
#include "stm32f10x_exti.h"
/* #include "stm32f10x_flash.h" */
/* #include "stm32f10x_fsmc.h" */
#include "stm32f10x_gpio.h"
/* #include "stm32f10x_i2c.h" */
/* #include "stm32f10x_iwdg.h" */
/* #include "stm32f10x_pwr.h" */
#include "stm32f10x_rcc.h"
/* #include "stm32f10x_rtc.h" */
/* #include "stm32f10x_sdio.h" */
/* #include "stm32f10x_spi.h" */
/* #include "stm32f10x_tim.h" */
#include "stm32f10x_usart.h"
/* #include "stm32f10x_wwdg.h" */
#include "misc.h"  /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */

プロジェクトのビルド(Debugを使って書き込みを行う場合)

以下のSTライブラリファイルをプロジェクトフォルダーにコピーします。

 core_cm3.c
 core_cm3.h
 misc.c
 misc.h
 stm32f10x.h
 stm32f10x_conf.h
 stm32f10x_exti.c →今回はなくてもビルドができました
 stm32f10x_exti.h →今回はなくてもビルドができました
 stm32f10x_gpio.c
 stm32f10x_gpio.h
 stm32f10x_rcc.c
 stm32f10x_rcc.h
 stm32f10x_usart.c
 stm32f10x_usart.h
 system_stm32f10x.c
 system_stm32f10x.h

.cファイルをAdd itemを使ってプロジェクトに追加。

Projcet

Project →Propertiesからライブラリの設定を行ってビルドします。
この際、Use OLD Precomplied LibraryをNoに設定。io_putcharとsmall_printfはコンパイル済みライブラリがリンクされます。

プロジェクトのビルド(Circle_Mgrを使って書き込みを行う場合)

Debugの手順で「objdebugもしくはobjrelease」フォルダーに生成される.oファイルをCircle_Mgrを使って書き込みを行おうとすると、以下のリンカーエラーが発生します。

[objrelease] >Circle_Mgr Aecho.o
Circle_Mgr: software for managing CircleOS applications.
Copyright Raisonance 2007-2009.

Connecting to RLink... OK
Connecting to target... OK
          Silicon Revision Id: 0x10016414.
          Option bytes: RDP=0xA5, USER=0xFF, WRP=0xFFFFFFFF
OK
Reading FAT table...
<No application>
... OK

Linking file echo.o...
Link Failed (file: 'echo.o')echo.o: In function `Application_Handler':
C:\Users\kenshi\Documents\Ride\Echo/Echo.c:61: undefined reference to `__io_getchar'
C:\Users\kenshi\Documents\Ride\Echo/Echo.c:65: undefined reference to `__io_putchar'
c:/tools/stm32/ride/arm-gcc/bin/../lib/gcc/arm-none-eabi/4.3.2/../../../../arm-none-eabi/lib/thumb
\libc.a(lib_a-sbrkr.o): In function `_sbrk_r':
sbrkr.c:(.text+0x12): undefined reference to `_sbrk'
c:/tools/stm32/ride/arm-gcc/bin/../lib/gcc/arm-none-eabi/4.3.2/../../../../arm-none-eabi/lib/thumb
\libc.a(lib_a-writer.o): In function `_write_r':
writer.c:(.text+0x16): undefined reference to `_write'
c:/tools/stm32/ride/arm-gcc/bin/../lib/gcc/arm-none-eabi/4.3.2/../../../../arm-none-eabi/lib/thumb
\libc.a(lib_a-closer.o): In function `_close_r':
closer.c:(.text+0x12): undefined reference to `_close'
  ~ 中略 ~
         !!! Error 103: Fatal error detected. Terminating program.

Circle_Mgrは、ソースが複数のファイルに分割された場合、.oファイルを指定した書き込みはできないことが分かりました。ForumのFAQに複数ファイルを使用する場合はアプリケーションではなくライブラリとしてビルドするべしとあります。io_putcharやsmall_printfといったコンパイル済みのライブラリを使う場合も、ソースをプロジェクトフォルダーに引っ張ってくる必要があります。

FAQにあるように、プロジェクトに、Add New Applicaton →New library to be builtで新規ライブラリの追加を行います。ライブラリプロジェクトは、親プロジェクト(キャプチャーの例では、Project 'Echo')の直下に作ります。アプリケーションプロジェクト(キャプチャーでは Echo)の子プロジェクトにするとビルドがエラーになるため注意が必要です。

Multifileproject

追加したライブラリプロジェクトにソースファイルを追加します。io_putcharやsmall_printfを使う場合は、ソースを追加する順番が重要です。呼び出し元が最後になるように並べないと、Circle_Mgrを使った書き込み時にリンカーのエラーが発生します。また、syscalls.cを最初にもってくる必要があります。そのため、配置の順番としては以下になります。

1) syscalls.c
2) STライブラリのソース
3) STM32F10X_IO_putchar.c
4) _SP_puts.c(printfを使う場合は、該当のソース)
5) echo.c(アプリケーションの処理を記述したソース)

ライブラリプロジェクトを追加する際に、以下のファイルが再度生成されて上書きされてしまうため注意が必要です。

  • Application.c: 事前にどこかにバックアップを作っておくか、アプリ本体をecho.cのような別名のファイルに記述します
  • stm32f10x_conf.h: STライブラリV2のファイルに上書きされるため、V3のファイルに戻す必要があります。

ライブラリプロジェクトをビルドすると生成される.libファイルをCircle_Mgrを使って以下のように書き込みます。

[Echo] >Circle_Mgr AEcho_lib.lib
Circle_Mgr: software for managing CircleOS applications.
Copyright Raisonance 2007-2009.

Connecting to RLink... OK
Connecting to target... OK
          Silicon Revision Id: 0x10016414.
          Option bytes: RDP=0xA5, USER=0xFF, WRP=0xFFFFFFFF
OK
Reading FAT table...
<No application>
... OK

Linking file Echo_lib.lib...
Link of Echo_lib.lib succeeded...
Hex file generated...
Blank-checking the FLASH area...OK
Programming file .\_tmp_.ld.hex to flash... OK

Registering application in FAT... OK

Closing com with RLink... OK

非常に面倒な手順です。mallocのような標準ライブラリを使う場合もlibcからはリンクできず、ソースを引っ張ってこないといけないと思われます。この点は早く改善してほしいものです。

試験プログラム

ターミナルソフトから読み込んだ文字をエコーバックする単純なプログラムコードを作って動作を確認しました。

enum MENU_code Application_Handler(void)
    {

    // TODO: Write your application handling here.
    int c;
   
    c = getchar();
    if ( c == 3 )       // c == CTRL-C
        return MENU_Quit();
    else
        putchar(c);

    // This routine will get called repeatedly by CircleOS, until we
    // return MENU_LEAVE

#if 1
    // If the button is pressed, the application is exited
    if(BUTTON_GetState() == BUTTON_PUSHED)
        {
        BUTTON_WaitForRelease();
        return MENU_Quit();
        }
#endif

    return MENU_CONTINUE;   // Returning MENU_LEAVE will quit to CircleOS
    }

今回修正・作成を行ったファイルは以下です。
 「USART2_Test.zip」をダウンロード

写真に示す通り、STM32 Primer2の拡張コネクターからケーブルを引き出して、USBシリアル変換モジュール経由でPCに接続しました。拡張コネクタの右下ピンが2番(GND)になります。

Primer2serial

今後の課題 - 割り込み処理

試験プログラムでは文字の読み取りに__io_getchar()関数を使っています。__io_getchar()はポーリング方式で、USART2のステータスレジスタ(USART SR)をチェックすることで受信あり・なしの判定と受信バイトの読み出しを行います。Application_Handlerの呼び出し周期が最短でも33msと長いため、ターミナルソフトにコピペで文字を流し込むと取りこぼしが発生します。

そのため、CircleOS配下で確実に受信を行うためには、受信を割り込み処理にしかつ一定量の受信バッファを持つ必要があります。次は受信の割り込み処理対応を行う予定です。割り込み処理は触ったことがないため、またはまりそうです。

STライブラリの構成を理解する上で、東京理科大学木村研究室さんのWikiを参考にさせていただきました。今回はSTライブラリのヘッダファイルをインクルードする順番が分からず、二重定義のエラーが出たりして右往左往していたところ、上記のWikiで多くの疑問が解消しました。せっかちなタイプでドキュメントを読む前にとにかくモノを動かしたくなるのですが、やはりドキュメントちゃんと読んで構成を把握した上で動かしてみるのが結果的に近道ですね。

« 2009年9月 | トップページ | 2009年11月 »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ