がれすたさんのDIY日記

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

STM32のタイマークロックについて

ここ最近タイマーを結構使うようになってクロック源について悩んだのでメモ

僕が普段使っているSTM32F7のタイマーのクロック源としてはAPB1、APB2の二つがある。


んでCubeでクロック設定をしている僕は気にしていなかったのだけどたくさん使うようになってどれがどこからもらってきているかがわからなくなった。
そんなわけでメモを残す。

うだうだ言ったけど結構簡単でRCC関連のレジスタをみれば書いてある。

f:id:gsmcustomeffects:20170827085138p:plain

f:id:gsmcustomeffects:20170827085206p:plain

ここに書いてある通りに割り振られてるのでCubeでこの辺いじると各種タイマーがどうなるのかわかると思う。

f:id:gsmcustomeffects:20170827085307p:plain

あと注意するのがRCC_DCKCFGR1レジスタのタイマークロックプリスケーラ(タイマーのカウントプリスケーラとは別のやつ)のビットをいじった時はこのへん読む

f:id:gsmcustomeffects:20170827085712p:plain

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変換できなくなるケースも有るとか

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