ダイソーの「リモコン付きテープライト」とSTM32のIRTIMとDMA burst

友人から、こんな面白いものがあるよとネタを振られたので遊んでみました。

ダイソーのLEDテープライトの外箱と中身
さらっと5V/817mAと書かれてるあたりどうかと思う。

物としては2023年頃から販売されているようです。3色LED×30個とリモコン付きで税抜500円ってすごくないですか? これでどうやって利益を出すのか。すごいです。ただ30個のLEDを個別に制御する、いわゆる addressable な仕様にはなっておらず、同時に指定した色にすることしかできません。さすがに NeoPixel ×30個では500円で収まらないと思う。

赤外線信号の解析

赤外線リモコンと言われたらまず信号を解析したくなりませんか? ということで何をするにもまずは解析からやってみました。超ざっくりまとめるとこんな感じ。

  • 仕様は普通の NEC format
  • Custom code は 0x00ef
  • 左上の button から横に 00, 01, 02, …… と連番を振っている分かりやすい並び
UPDOWNOFFON
0x00ef00ff0x00ef01fe0x00ef02fd0x00ef03fc
RGBW
0x00ef04fb0x00ef05fa0x00ef06f90x00ef07f8
FLASH
0x00ef08f70x00ef09f60x00ef0af50x00ef0bf4
STROBE
0x00ef0cf30x00ef0df20x00ef0ef10x00ef0ff0
FADE
0x00ef10ef0x00ef11ee0x00ef12ed0x00ef13ec
SMOOTH
0x00ef14eb0x00ef15ea0x00ef16e90x00ef17e8
Controller の button と対応する command の一覧。NEC format は大きい byte から LSB first で送信するので注意

なんとなくの想像ですが、きっとこういう汎用的に使えるICが出回ってるんでしょうね。で、実装側でそれぞれの信号がどの機能に対応するのか決めてるんだと思います。

STM32 の IRTIM

STM32 が好き (というか STM32 HAL と STM32CubeMX に飼い馴らされたダメ人間) という理由から STM32 で制御することにしましたが、IRTIM を使うのは初めてです。

IRTIM は TIM16 と TIM17 の論理積として動作します。STMicroElectronics の説明によると、TIM16 は送信する0/1の PWM の波形を作るために、TIM17は副搬送波(今回は 38kHz)を作るために使うようです。今回は NUCLEO-G031K8 を64MHzで動かすことにしたので、設定値はそれぞれ以下としました。

  • TIM16……Prescaler: 35967, Counter Period: 24 (仮)
  • TIM17……Prescaler: 40, Counter Period: 40 (64MHz/((40+1)*(40+1)) ≒ 38kHz)
  • TIM16とTIM17を有効にしたらIRTIMを有効にできる
    • output polarity: Polarity inverted (STM32G0固有機能、出力に抵抗とLEDを直結するときに便利)
    • IR Modulation Envelope signal (STM32G0固有機能、UART でも送出できるらしい)

IRTIM を有効にしながら、TIM16とTIM17をそれぞれPA6とPA7に出力することができます。Logic analyzer による動作確認に便利です。

STM32 の DMA burst 転送

TIM16 の ARR (Auto Reload Register) と CCR1 (Capture/Compare Register 1) を同時に更新する必要があり、間に RCR (Repetition Counter Register) が挟まっているので、単純な DMA ではなく DMA burst が必要になります。が、あまり設定する箇所はありません。

  • DMA Request timing: TIM16_UP
  • Direction: Memory To Peripheral
  • Data Width: Word
  • Increment Address: Memoryのみ有効

Code

以上の設定から、書くべき code はこんな感じになります。ここでは RGB を循環させてみました。Comment にも書いてますが、明示的に HAL_TIM_PeriodElapsedCallback() の中で HAL_TIM_PWM_Stop() と HAL_TIM_DMABurst_WriteStop() を呼んでやる必要があります。

// DMA burst 転送は終了後に停止しないと次の転送ができない
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_TIM_PWM_Stop(&htim16, TIM_CHANNEL_1);
	HAL_TIM_DMABurst_WriteStop(&htim16, TIM_DMA_UPDATE);
}


// size of txbuffer must be 102 or more.
void txdata(uint32_t command, uint32_t* txbuffer)
{
	  *txbuffer       = 24; // ARR = t_on + t_off
	  *(txbuffer + 1) =  0; // RCR
	  *(txbuffer + 2) = 16; // CCR1 = t_on
	  for(int byteCount = 0; byteCount < 4; byteCount++)
	  {
		  for(int bitCount = 0; bitCount < 8; bitCount++)
		  {
			  *(txbuffer + 3 + (byteCount * 8 + bitCount) * 3    ) = ((((command >> ((3 - byteCount) * 8)) & 0xff) >> bitCount) & 1) == 0 ? 1 : 3;
			  *(txbuffer + 3 + (byteCount * 8 + bitCount) * 3 + 1) = 0;
			  *(txbuffer + 3 + (byteCount * 8 + bitCount) * 3 + 2) = 1;
		  }
	  }
	  *(txbuffer +  99) = 100;
	  *(txbuffer + 100) = 0;
	  *(txbuffer + 101) = 1;
}

int main(void)
{
  // 中略
  /* USER CODE BEGIN 2 */

  // 副搬送波
  __HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 13);
  HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);

  uint32_t txbuffer[(1 + 16 + 8 + 8 + 1) * 3] = {0}; // leader + customer code + data + ^data + tail = 102
  uint32_t commands[] = {0x00EF04FB, 0x00EF05FA, 0x00EF06F9}; // Red, Green, Blue

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  for(int index = 0; index < 3; index++)
	  {
		  txdata(commands[index], txbuffer);
		  while(HAL_TIM_DMABurstState(&htim16) != HAL_DMA_BURST_STATE_READY) {}
		  if(HAL_TIM_DMABurst_MultiWriteStart(
				  &htim16,
				  TIM_DMABASE_ARR,
				  TIM_DMA_UPDATE,
				  (uint32_t *)txbuffer,
				  TIM_DMABURSTLENGTH_3TRANSFERS,
				  sizeof(txbuffer) / sizeof(uint32_t)) != HAL_OK)
		  {
			  Error_Handler();
		  }
		  if(HAL_TIM_PWM_Start(&htim16, TIM_CHANNEL_1) != HAL_OK)
		  {
			  Error_Handler();
		  }
		  while(HAL_TIM_DMABurstState(&htim16) == HAL_DMA_BURST_STATE_BUSY) {}
	  }
    /* USER CODE END WHILE */

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

できあがり

TIM17、TIM16、IRTIM それぞれの出力と protocol decoder の結果を PulseView で観察したところ、上々の出来です。ただしRGBを循環させるとものすごいギラギラした感じになるのであまりおすすめしません。

参考資料


Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です