がれすたさんのDIY日記

電子回路、Python、組み込みシステム開発、自作エフェクターを語るblog

STM32のUSBCDCでMicroShellを動かす

前回まででUSBCDC(コミュニケーションデバイスクラス)でVCP(VirtualCOMPort)の動作は確認できたので今回はこれにMicroShellを使える状態していく作業になります。
CDCのやり方はこの記事みてください。
gsmcustomeffects.hatenablog.com

やることは

  • 1文字送受信APIの準備
  • 受信バッファサイズの設定
続きを読む

WEAKシンボルについて

ここ最近ARM系のお勉強を頑張っています。


今日はWEAK修飾子についてです。
STを例に話すと割り込み後に呼ばれるコールバックってのがWEAKマクロを使って記述されてます。
定義先に行くと

__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);

  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */

}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	  //i2c_lcd_writeCommand(ClearDisplay);
	  i2c_lcd_writeCommand(ReturnHome);
	  HAL_Delay(2);
	  xprintf("Count:%d",count);
	  count++;
}


のように記述されている。

これをMainで呼び出すとこのように使う。



要は同じ関数が二個あったらWEAKがついてる側が譲るということである。
こうすることで関数の上書き的なことができる。これがないとmain文で定義しなければコンパイルエラーになってしまうのでこうやって回避しているわけである。


ちなみにI2C系のデバイスライブラリ作るときもI2Cの送信APIを上書きで定義できるようにすれば汎用性を持たせられるので便利

__weak void i2c_write(){
	//This Section  is User function
}
void i2c_write(){
	HAL_I2C_Master_Transmit(&hi2c1,addri2c,(uint8_t*)data,2,0xFF);
}

これでi2c_lcdの制御部分を記述してます。
1文字送信コマンドが2byteなのでこういった回りくどいことしてます。
f:id:gsmcustomeffects:20170603224309j:plain

STM32でSPI通信をやってみる1(ポーリングでマイコン同士で通信編)

今回はCubeHALライブラリを用いてSPI通信をやってみる記事です。

STM32のSPIは全二重、半二重、単方向、マルチマスターモードなどいろいろ対応してますが今回は一般的な全二重でやってみようと思います。

f:id:gsmcustomeffects:20170510195127p:plain

Pin Name description
MOSI マウターアウトスレーブイン
MISO マスターインスレーブアウト
SCK シリアルクロック
NSS スレーブセレクトピン
続きを読む

ZOOM MS50g,MS70CDRのMIDI制御

今回はZOOM社のマルチストンプのMIDI制御についてです。

一年前ぐらいにUSBHOSTでMIDI制御しましたがいろいろ発見があったので今回こうして書いているわけです。


続きを読む

STM32でADCをやってみる2(DMAを使ったレギュラ変換)

前回は単一チャンネルの変換を行ったが今回はDMAを使ったものをやって行こうと思う。

とりあえずADCおさらい

ADCは変換終了フラグ(EOC)を見てDRレジスタを見に行くことで変換データを得ることができる。

f:id:gsmcustomeffects:20170409035725p:plain

上記の図のようにシーケンスの終了でもフラグが立つ(EOS)
二つのフラグを見てソフトウエア的に行うこともできるがタイミングがずれるとDRのデータが上書きされてしまう。

それを防ぐ仕組みにオーバーランというものある。
これはデータを保護できるがそのあとの変換値は破棄されてしまうので注意が必要だ

f:id:gsmcustomeffects:20170409040231p:plain

そのため複数チャンネルを変換する場合EOCフラグをトリガとしてDMAでレジスタ→メモリを実現したほうがソフト的な介在がなくなるので楽に処理できる。

マニュアル曰く(RM0316 345ページ)

変換されたチャネルの値は特定のデータレジスタに格納されるので、複数のチャネルの変換には
DMA の使用が便利です。これによって、ADCx_DR レジスタにすでに格納されているデータの損失を防ぐことができます。
DMA モードが有効なとき(ADCx_CFGR レジスタの DMAEN ビットがシングル ADC モードで 1 にセットされている場合、またはデュアル ADC モードで MDMAが0b00 以外に設定されている場合)、
各チャネルの変換後、DMA リクエストが生成されます。これにより、変換データを ADCx_DR レジスタからソフトウェアで選択した場所へ転送することができます。
これにもかかわらず、DMA が DMA 転送リクエストを時間内に処理できなかったためにオーバーランが発生した場合(OVR=1)、ADC は DMA リクエストの生成を停止し、新しい変換に対応するデータ
は DMA によって転送されません。
これは、RAM に転送されるすべてのデータを有効とみなすことができることを意味します。

要するにCH変換ごとにDMAリクエストが生成されるということ。
オーバーランが発生するとDMAがとまる。
OVRMODを適切に処理するか頻繁に止まるようならDMAを時間内に処理できるように変換自体の速度を下げておくなど工夫が必要だ。

DMAのモードには

  • DMA ワンショットモード(DMACFG=0)
  • DMA サーキュラモード(DMACFG=1)

の2つがあり

ワンショットは一回きりの転送になります

f:id:gsmcustomeffects:20170409042453p:plain

一方サーキュラは連続的にADCのCH変換が終わるたびにDMAリクエストが生成するので連続的なストリームが構成できる。

f:id:gsmcustomeffects:20170409042740p:plain

さっそくやっていく

CubeMXでADCを設定していく。

f:id:gsmcustomeffects:20170409043833p:plain

ADCの詳細設定

f:id:gsmcustomeffects:20170409043955p:plain

DMAの設定

f:id:gsmcustomeffects:20170409044035p:plain

ここでアドレスのインクリメントにチェックを入れておくといい感じに配列に格納できる。

ハードウエア

前回と同じでこれにPA_5、PA_4を追加して2CHを読んでいる点だ

f:id:gsmcustomeffects:20170408045403j:plain

コード

参考までに動いたコードを示しておく
ADC終了時に呼ばれるコールバック内にprintfを入れてUART出力している。


動作例

Teratermではこんな感じに2CH読めている。

f:id:gsmcustomeffects:20170409050119p:plain

Expressionではこんな感じ

f:id:gsmcustomeffects:20170409050225p:plain

まとめ

3日間ぐらいかけてADCやってみたんだけど何だかんだDMAを使ったのが一番簡単だねぇ
DMAといったら結構難しいイメージあったけどCubeMXのおかげでほとんど設定してくれるのでかなり楽ですね。

3日間一緒に検討してくれたリアルテック先生には感謝です。

http://realteck-blog.netlify.com/2017/04/08/f031%E3%81%AEadc%E9%80%A3%E7%B6%9A%E5%A4%89%E6%8F%9B%E3%81%8C/realteck-blog.netlify.com

2017/5/18追記

お恥ずかしいお話なのですがオカダといろいろやってて複数のADCを使うとMainにブレーク立てても帰ってこないと2日間ぐらい悩みました。

f:id:gsmcustomeffects:20170518003058p:plain

んで上記の図のようにこいつのADCには低速チャネルもあるんです。
12ビット精度で変換する場合最低でも14サイクルかかります
f:id:gsmcustomeffects:20170518003238p:plain

高速の場合Cubeで19.5サイクルにすればよいのですが低速の部分も19.5にしてました・・・・・ほんと雑魚wwww

なので皆さんはこんなバカなことで時間とかしちゃだめですよ!

直すとちゃんと動くf:id:gsmcustomeffects:20170518003433p:plain

追記 2019/3/12

自分の場合上記のコードで動いたのですがshyachiさんより指摘があったので載せときます

 /* USER CODE BEGIN 2 */
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
    {
      Error_Handler();
    }

  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }

の2つの関数はMX_ADC2_Initにてコールされているので呼ばなくて良さそうです。
特にHAL_ADC_ConfigChannelの方は呼ぶと2ch変換できなくなるケースも有るとか

動かない人はこの辺もお試しください

STM32でADCをやってみる1(レギュラ変換)

今回は一番簡単なやつやってみます。

やったことを雑に書くとこんな感じ

  • レギュラ変換
  • ADC2の1CHだけを使った連続変換
  • PB4ピンにポテンショメータをつなぎそれを読む
  • 変換値をUARTでTeratermに表示する。

CubeMXでの設定

今回は一個しか読まないのでこのようにPA4にアサインする
一応UARTも使ってるのでそれも設定する

f:id:gsmcustomeffects:20170408045027p:plain

各種設定はこんな感じ

f:id:gsmcustomeffects:20170408045044p:plain

要所要所説明挟むとこんなもん

ADC_Setting

設定 機能 説明
mode independent mode 独立モードで動く、その他速度を上げるDualModeがある
Clock Prescaler 非同期、1~4分周 読んで字のごとくクロック分周器
Resolution 6~12bit ADCの分解能
Data Alignment 右詰め、左詰め データの詰め方
Scan Coversion Enable,Disable 一回のシーケンスですべてのCHを変換するかどうか
Continuos Conversion Mode Enable,Disable 連続変換
Dis Continuos Conversion Mode Enable,Disable 不連続変換
End Of Conversion Selection シングルごとかシーケンスごとか両方か 変換終了フラグの有無 データシートEOCフラグの部分読んだほうが図もあってわかりやすい
Low Power Auto Wait Enable,Disable 自動遅延変換モードの有無。ADC オーバーランが発生するリスクのある低周波数のクロックで動作しているアプリケーションのパフォーマンスを最適化する場合に有効

ADC_Regular_Conversion_Mode

設定 機能 説明
Enable Regular Conversion Enable,Disable レギュラー変換の有効化
Number of conversion 変換数 チャンネル数分設定できる
External Trigger Conversion Source ソフトおよびタイマートリガ 変換トリガソースの設定。 タイマーとかに連動できる
External Trigger Conversion Edge 立ち上がりか立下りかソフトか 外部割り込みの設定

Rankの設定

RankはCH分だけ個別設定ができる

設定 機能 説明
Channel チャンネル数分設定できる 設定したいCHを入れる
SamplingTime 1.5~601.5サイクル サンプリングタイム
OffsetNumber 1~4 オフセットするCHの選択
Offset 1~4056 変換値のオフセット

ハード構成

一応紹介しておく
POTの2番ピンをA3ピンにつないでいるだけだが

f:id:gsmcustomeffects:20170408045403j:plain

コードを書いていく

参考までにコードを貼っておく