STM32F1 / F7は、HALライブラリDMAモードを使用して、PWMの詳細な説明を出力します(正確な量と調整可能な周期およびデューティサイクルを出力します)

1.STM32のDMAPWM原理

当初、STM32がステッピングモーターを制御するための正確なパルス出力数をどのように実現できるのか疑問に思いましたが、DMA-PWMモードがあることを知ったのはWS2812Bランプビードドライバープログラムまででした。DMAを使用してPWMを出力すると、パルス数を正確に制御でき、パルス周期とデューティサイクルを正確に制御できます。さらに重要なことに、DMA送信を使用してもCPUリソースを消費しません。そこで、インターネットでリソースやチュートリアルを検索しましたが、残念ながら、オンラインチュートリアルが不明確であるか、コードが不完全であるか、表面についてのみ説明し、原理を説明せずにコードを投稿するだけです。DIYの古代のモットーに従い、リファレンスマニュアルを読み、DMAの章からタイマーの章を読み、実際のコードと組み合わせて、最終的にいくつかの手がかりを見つけ、その過程で発生した問題を共有しました。

1.DMAの概要

公式の解釈:DMA、フルネームは次のとおりです:ダイレクトメモリアクセス、つまりダイレクトメモリアクセス。DMA伝送方式では、CPUが直接伝送を制御する必要がなく、シーンを保持して復元するための割り込み処理方式もありません。ハードウェアは、RAMおよびI / Oデバイスの直接データ伝送パスを開きます。 CPUの効率を大幅に向上させます。次の図は、インターネットのリファレンスマニュアルから抜粋した一般的なDMAブロック図です。

ここに画像の説明を挿入
STM32F1シリーズには2つのDMAがあり、それぞれ7チャネルと5チャネル(チャネル)です。F7シリーズの各DMAには8つのデータストリーム(ストリーム)があり、各データストリームは8つのチャネル(チャネル)に対応します。2つの一連の式のわずかな違いは次のとおりです。F1のチャネルはF7のストリームに対応する必要があります。たとえば、DMA1のチャネル対応表は次のようになります。STM32のADC、SPI、IIS、USART、IIC、TIM、DAC、およびその他のデータ送信周辺機器はすべてDMAモード送信に設定できます。手動構成時にテーブルを検索してチャネルを選択できます。もちろん、Cubemxを使用する場合ツール、それは自動的に上に選択されます。
ここに画像の説明を挿入DMA転送の利点は何ですか?たとえば、シリアル通信では、HAL_UART_Transmit()とHAL_UART_Transmit_DMA()を使用してデータを送信します。前者は通常モードを使用し、CPUはデータ転送が完了するまで実行関数に入り、終了してから実行します。次の指示。後者はDMAを使用して転送します。DMAが転送を開始すると、CPUはそれを無視し、他の命令を直接実行します。CPUは何をしますか?DMA転送完了、ハーフ転送完了、転送エラーなどの割り込みを処理するか、レジスタに問い合わせてDMA転送のステータスを確認するだけで済みます。あっという間に起きてしまいました!

2. DMA出力PWMはどうなっていますか?

DMAを使用してデータを転送する方法を理解するのは簡単ですが、DMAがPWMパルスの数とデューティサイクルを制御できるのはなぜですか?ここで本質に戻ります。DMAがPWM出力を制御する過程で、DMAはデータを送信しますが、送信するのは比較値、つまりキャプチャ/比較レジスタ(TIMx_CCRx)の値です。この値は必要ありません。自動リロードレジスタ(TIMx_ARR)の値によって、それぞれ周期とデューティサイクルが決まります。マニュアルのタイマーのDMA連続転送モードの説明をご覧ください。

ここに画像の説明を挿入
黄色の部分に注意してください、更新イベントとは何ですか?要約すると、アップカウントモードでは、カウントが自動リロード値に達すると更新イベント(オーバーフロー)が発生し、その後、カウントがゼロから再開されます。つまり、各PWM波が終了すると、DMAによって転送されたデータに比較値が自動的に設定されます。この例で設定された1msの周期を例として取り上げ、send_Buf [] = {10,20,30、…、100}を設定します。最終的な波形は、それぞれ10,20,30、…100usの高レベル時間の10個の方形波です。 。
とても簡単です!

3.HALライブラリDMA構成PWMのいくつかの機能

正直なところ、HALライブラリの使用はまだ少しひねりがあり、多くの操作はさまざまなレベルでカプセル化されています。数文のコードを使用して登録できます。HALライブラリがHALライブラリに到達したら、次のことを行う必要があります。いくつかの関数を呼び出して数ターンしますが、これは一般的な傾向でもあり、最下層をカプセル化します。ユーザーがアプリケーションに集中できるようにします。最近、Cubemxを使用してコードを自動的に生成する感覚は、それがとても香りがよいということです!

stm32F7xx_hal_tim.hで関数定義を転送します。以下は、PWMを開始および停止するためのブロッキングモード、割り込みモード、およびDMAモードです。

/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);

以下は、割り込みコールバック関数の宣言です。

/* Callback in non blocking modes (Interrupt and DMA) *************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_ErrorCallback(TIM_HandleTypeDef *htim);

ここではvoid HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim);
、PWM出力が完了するたびにこの関数呼び出すことに注意します。割り込みではHAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel)、DMA転送停止するために呼び出す必要があります。そうしないと、DMA転送が自動的に停止しません。

2.STM32CubeMx構成DMAPWM

テストの例としてSTM32F1およびF7シリーズボードを取り上げます。テスト後、2つの構成は基本的に同じであり、結果も同じであるため、ここではF1を例として説明します。

図に示すように、STM32F103ZET6に基づいて新しいプロジェクトを作成するには、最初にクロックを構成し、システムクロックを最大72MHzに設定します。

ここに画像の説明を挿入
次にタイマーを設定します。T2を使用します。4つのチャネルすべてが選択され、必要に応じて実行できます。周波数分割係数は71に設定されています。つまり、周波数は72で除算され、pwm周波数は1MHz、自動リロード値は1000、サイクルは1msです。

ここに画像の説明を挿入
次に、図に示すようにDMAを設定します。4つのチャネルDMAがすべて選択されています。ここでは、CH2とCH4がチャネルを共有し、当面はそのままにしておきます。

ここに画像の説明を挿入
この時点でDMA割り込みがオンになっていることがわかります。

ここに画像の説明を挿入
また、ST-Linkを使用してプログラムをダウンロードする場合、図に示すように、この場所ではデバッグモードがシリアルワイヤに設定されていることに注意してください。それ以外の場合、ST-Linkはプログラムを1回しかダウンロードできません。

ここに画像の説明を挿入
設定が完了したので、をクリックするだけGENERATE CODEです。

3.波形デバッグプロセスの分析

プロジェクトを開くと、TIMとDMAの初期化関数が表示されます。ここで、メイン関数にコードを追加し、HAL_TIM_PWM_Start関数を呼び出して、連続波形を正常に出力します。

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1)

__HAL_TIM_SET_COMPARE関数を呼び出して、デューティサイクルを変更します

__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,200);

200に設定すると、高レベル時間は200us、デューティサイクルは200/1000になります。

DMAを使用するため、main.cで送信データバッファを定義します。

#define NUM 21
uint32_t send_Buf[NUM] = {
    
    0};

main関数に次のコードを追加します

for (i = 0; i < NUM; i++)
{
    
    
 	send_Buf[i] = 20 * (i + 1);
}

while (1)
{
    
    
	 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); 
	 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET); 
	 HAL_Delay(200);
	 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); 
	 HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET); 
	 HAL_Delay(200);
	 HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_3,(uint32_t*)send_Buf,NUM);
}

次の関数を追加します

// PWM DMA 完成回调函数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    
    
	HAL_TIM_PWM_Stop_DMA(&htim2, TIM_CHANNEL_3);
}

これは、HAL_TIM_PWM_Stop_DMAPWM出力停止するためにコールバック関数で関数を呼び出す必要性の最初の部分です
理論的には、期待される波形は必要に応じて出力されます。つまり、デューティサイクルが増加する21波です。しかし残念ながら、私の波は次のようになっています。

ここに画像の説明を挿入
3つの問題:
(1)データが半分しかない;
(2)波の周期が通常の2倍になる(2ms)
(3)最後のデータが最前線に流れた。

怒っている...

そこで、バグの調整を開始しました。最初の問題が発見されました。HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);ファンクションの送信データポインタが32ビットを指しているため、send_Bufも32ビットとして定義されていますが、DMA転送の設定はハーフワードで16ビットです。図に示されています。

ここに画像の説明を挿入

つまり、ポインタがuint32_t * pDataから1ビット移動するたびに、アドレスは2バイトオフセットされます。送信したいデータは{sendBuf [0]、sendBuf [1]、…、sendBuf [20]}ですが、実際に送信されるデータは
sendBuf [0] low 16であるため、これで上記の1番目と2番目の問題を説明できます。ビット
sendBuf [0]上位16ビット(つまり0)

sendBuf [9]下位16ビット
sendBuf [9]上位16ビット(つまり0)
sendBuf [10]下位16ビット

次に、送信ワード幅をワード(32)ビットに変更するか、send_Bufをuint16_tタイプに変更すると、図に示すように、テスト結果が正しくなります。

ここに画像の説明を挿入
したがって、友人に思い出させてください
。DMA送信ビット幅と定義されたバッファビット幅は一貫している必要があります。
DMA送信ビット幅と定義されたバッファビット幅は一貫している必要があります!
DMA送信ビット幅と定義されたバッファビット幅は一貫している必要があります!

問題3はまだ存在するため、最後のデータを0に変更してみてください
追加されたsend_Buf[NUM - 1] = 0;波形は正常です。

ここに画像の説明を挿入
DMA送信の開始時と終了時の不安定な要因によるものかどうかは不明ですが、この場合、毎回通常のデータの後に1つ以上の0を追加するだけで十分です。の使用には影響しません。

これまでのところ、DMA制御PWM出力は成功しており、次の記事ではHAL-DMA-PWMを使用してWS2812ランプビーズを点灯します。

11.22アップデート、一部の友人はプライベートメッセージやコメントで完全なルーチンを持ちたいと思っています。私はそれをCSDNにアップロードし、無料でダウンロードできます。それを好きになることを忘れないでください!

トランスミッションゲート:STM32F1はHALライブラリDMAメソッドを使用してPWMルーチンを出力します(正確な量と調整可能な周期とデューティサイクルを出力します)

おすすめ

転載: blog.csdn.net/qq_30267617/article/details/109466698