2020年8月29日土曜日

【STM32】HALライブラリ色々(タイマ割込み、PWM出力、AD変換、シリアル通信、エンコーダモード)

どうもcaketetuです。
前回、HALライブラリでLチカできました。今回はその他使いそうな機能のテストプログラムのメモです。


タイマ割込み

特別な設定なく一定間隔でのタイマ割込みをかけます。TIM15を使用し、間隔は1sです。
タイマ割込みでLチカするのでIO出力の設定も必要になります。IO出力は前回参照です。

CubeMX設定

赤部分がデフォルトから変更した箇所です。割込み周波数は1Hzなのでソース周波数(ここでは32MHz)をPrescaler(10000カウント)で割り、さらにそれをCounter Period(3200カウント)分カウントアップすると1Hzになります。画像は間違ってますね…Counter Period=3199が正しいです。

ここで設定値は0からのカウントなので例えば10000カウントしたければ10000-1=9999を設定します。10000-1と書いても大丈夫なようです。




サンプルプログラム

割込みでLEDをON-OFFします。割込みをmain.cに持ってきたかったので新しく関数を作りstm32f3xx.it.cで呼び出すようにしています。

//Private user code(60行目付近)
/* USER CODE BEGIN 0 */
void tim15_tick(void){
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_2);
}

//stm32f3xx_it.c内
void TIM1_BRK_TIM15_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_BRK_TIM15_IRQn 0 */
  tim15_tick();		//関数を追加
  /* USER CODE END TIM1_BRK_TIM15_IRQn 0 */
  HAL_TIM_IRQHandler(&htim15);
  /* USER CODE BEGIN TIM1_BRK_TIM15_IRQn 1 */

  /* USER CODE END TIM1_BRK_TIM15_IRQn 1 */
}

//mainループ(100行目付近)
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim15);	//タイマスタート
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */



PWM出力

TIM1をPWM出力に割り当てます。周期は1kHzです。PWMでLEDの光量を操作します。Prescalerは32カウント、Counter Periodは1000カウントで1KHzですね。

CubeMX設定

赤部分がデフォルトから変更した箇所です。

サンプルプログラム

上記でCounter Period=0~999で設定したので、この間の数値でデューティ比を決めます。
50%なら499になりますね。
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);		//PWMスタート
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);		//PWMスタート
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
		for(int i=0; i<999; i++){
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, i);		//PWM出力
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 999-i);	//PWM出力
			HAL_Delay(1);
		}

		for(int i=0; i<999; i++){
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 999-i);	//PWM出力
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, i);		//PWM出力
			HAL_Delay(1);
		}
  }
  /* USER CODE END 3 */



AD変換

ADC1、CH4でAD変換します。デバックにPWMを使うので上記方法で設定しておきます。
今回は最も簡単なワンショットでの測定です。ADCの分解能が12bitなのでPWMのCounter periodは4095に設定します。これでAD値をそのままぶち込めばぴったりDuty比0~100になりますね。

CubeMX設定

赤部分がデフォルトから変更した箇所です。

サンプルプログラム

AD変換値をそのままPWM出力しLEDを点灯させます。

  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	//AD変換
	HAL_ADC_Start(&hadc1);
	HAL_ADC_PollForConversion(&hadc1, 100);
	HAL_ADC_Stop(&hadc1);
	int ad_val = HAL_ADC_GetValue(&hadc1);

	//PWM出力
	__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, ad_val);		//PWM出力
  }
  /* USER CODE END 3 */



シリアル通信

送信値をそのまま返すエコーバックを作ってみます。UART2を使用し、受信は割込みで送信はメイン関数で行います。

CubeMX設定

赤部分がデフォルトから変更した箇所です。NVIC設定で割込みを有効にします。




サンプルプログラム

//グローバル変数定義(50行目付近)
/* USER CODE BEGIN PV */
//シリアル関連
char recv_buf[256];
char recv_data[256];
char recv_data_len=0;
/* USER CODE END PV */

//mainループ(100行目付近)
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart1, &recv_buf, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	//データがあるとき
	if(recv_data_len){
		HAL_Delay(10);
		//データをすべて読み出し
		HAL_UART_Transmit( &huart1, recv_data, recv_data_len, 0xFFFF);
		recv_data_len=0;
	}
  }
  /* USER CODE END 3 */
}

//コールバック関数(230行目付近)
/* USER CODE BEGIN 4 */
//シリアル受信割込みコールバック関数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  HAL_UART_Receive_IT(&huart1, &recv_buf, 1);
  recv_data[recv_data_len] =  recv_buf[0];
  recv_data_len++;
}
/* USER CODE END 4 */



エンコーダモード

タイマ機能のエンコーダモードを使用し、エンコーダ入力をカウントします。使用しているエンコーダはFaulhaver1524モータについてきたIEH2-4096です。これはエンコーダが二相あり回転方向が分かるやつなのでEncoder Modeを二相に設定します。

CubeMX設定

赤部分がデフォルトから変更した箇所です。今回はTIM2、TIM3でモータ二つ分の入力をとってみます。どちらも設定は同じです。デバックでシリアルを使うので上記の要領で設定します。




サンプルプログラム

カウント値をシリアルで送信します。値が増えていれば正転、減っていれば逆転になります。

  /* USER CODE BEGIN 2 */
  HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL); //読み取りスタート
  HAL_TIM_Encoder_Start(&htim3,TIM_CHANNEL_ALL); //読み取りスタート
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	int enc1 = TIM2 -> CNT; 		//TIM2の値を取り出してcntに格納
	int enc2 = TIM3 -> CNT; 		//TIM3の値を取り出してcntに格納

	//シリアル書き出し
	char buf[100];
	sprintf(buf , "%d %d \r\n",enc1, enc2 );
	HAL_UART_Transmit( &huart1, buf, strlen(buf) + 1, 0xFFFF);
	HAL_Delay(100);
  }
  /* USER CODE END 3 */
}


動作の様子






今回は以上です。とりあえず試してみたい機能はできたと思います。次回から本格的なハードウェア制御用マイコンとして使っていきたいですね。

0 件のコメント:

コメントを投稿