« Ride7開発環境のデバック制限解除ライセンス | トップページ | mbedプロトタイピングツール »

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で多くの疑問が解消しました。せっかちなタイプでドキュメントを読む前にとにかくモノを動かしたくなるのですが、やはりドキュメントちゃんと読んで構成を把握した上で動かしてみるのが結果的に近道ですね。

« Ride7開発環境のデバック制限解除ライセンス | トップページ | mbedプロトタイピングツール »

STM32-ARM」カテゴリの記事

コメント

この記事へのコメントは終了しました。

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      
無料ブログはココログ