1. NECフォーマットの概要
NECプロトコルは 38 kHz キャリアを用いた赤外線通信規格である。受信側で観測すると、以下の時間幅でビットが表現される。
- 論理 1:560 µs の Low 後 1.68 ms の High(合計 2.24 ms)
- 論理 0:560 µs の Low 後 560 µs の High(合計 1.12 ms)
1フレームは次で構成される。
- リーダーコード:9 ms Low → 4.5 ms High
- アドレス(8 bit、LSB ファースト)
- アドレス反転(8 bit)
- コマンド(8 bit)
- コマンド反転(8 bit)
ボタンを押し続けると「リピートコード」が送信される:9 ms Low → 2.25 ms High → 560 µs Low → 約 96 ms High。
2. ハードウェア構成
赤外線受信モジュール(VS1838B 等)の出力を TIMx CH1 に接続し、入力キャプチャで立ち上がり/立ち下がりエッジを取得する。
3. タイマー設定
CubeMX で TIM1 CH1 を Input Capture に設定し、両エッジ検出にする。プリスケーラは 1 µs カウントになるよう調整(例:APB2=72 MHz → 72-1)。
4. 変数とバッファ
volatile uint8_t edge; // 0:立ち上がり 1:立ち下がり
volatile uint32_t t_rise; // 立ち上がり時刻
volatile uint32_t t_fall; // 立ち下がり時刻
volatile uint32_t width_us; // パルス幅[µs]
uint32_t ir_buf[34]; // 最大34個格納
uint8_t ir_idx = 0;
uint8_t frame_ok = 0;
5. 入力キャプチャ割り込み
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) return;
if (edge == 0) { // 立ち上がり
t_rise = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1,
TIM_INPUTCHANNELPOLARITY_FALLING);
edge = 1;
} else { // 立ち下がり
t_fall = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1,
TIM_INPUTCHANNELPOLARITY_RISING);
edge = 0;
width_us = (t_fall >= t_rise) ? (t_fall - t_rise)
: (0xFFFF - t_rise + t_fall);
if (width_us > 4000 && width_us < 5000) { // リーダー検出
ir_idx = 0;
ir_buf[ir_idx++] = width_us;
} else if (ir_idx > 0 && ir_idx < 34) {
ir_buf[ir_idx++] = width_us;
if (ir_idx == 34) frame_ok = 1;
}
}
}
6. メインループでのデコード
uint8_t decode_nec(uint32_t *buf)
{
uint8_t cmd = 0;
for (int i = 0; i < 8; i++) {
uint32_t high = buf[17 + i]; // コマンドビット開始位置
if (high > 1400 && high < 1800)
cmd |= (1 << i);
else if (high < 800) // 論理0
cmd |= (0 << i);
else
return 0xFF; // エラー
}
return cmd;
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM1_Init();
HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);
while (1) {
if (frame_ok) {
uint8_t key = decode_nec(ir_buf);
switch (key) {
case 0x18: move_forward(); break;
case 0x4A: move_backward(); break;
case 0x10: turn_left(); break;
case 0x5A: turn_right(); break;
}
frame_ok = 0;
}
}
}
7. リピートコードの扱い
リピートコードは High 部が 2.25 ms なので、width_us が 2000–2500 µs の場合は前回のコマンドを再実行するだけでよい。