
CH32V003J4M6を使用した赤外線リモコン受信実験
はじめに
最近は格安マイコンのCH32Vをで遊んでいるのですが、何か製作するときに役立つものをやってみようと思い、今回は8ピンのCH32V003J4M6とch32v003funを使用して赤外線リモコンので信号をを受信するプログラムを作成してみました。
使用部品・ 回路について
使用部品
使用部品は表の部品を使用しています。
部品名 | 型番・値 |
---|---|
マイコン | CH32V003J4M6 |
赤外線リモコン受信モジュール | SPS-440 |
npn型トランジスタ | 2SC1815 |
抵抗 | 10kΩ |
抵抗 | 4.7kΩ |
抵抗 | 1kΩ |
ブラケットLED | SLP-711H |
回路
回路図を下に示します。
SPS-440は電源が5V必要となります。CH32V003は5Vでも動作するのでそのままSPS-440の出力を入力しても良いのですが、CH32V203のように3.3V電源の電源でも使用できるようにトランジスタを入れて、レベル変換をしています。また、SPS-440の出力も反転させています。
プログラムについて
今回は公式SDKを使用せず、ch32v003funを使用しました。公式SDKは色々な周辺機能も入れてしまうのでサイズが大きくなること、CH32V003もの容量も少ないので、ほとんどレジスタ操作で設定をするch32v003funにしました。
今回は家電協フォーマットとsonyフォーマットの2種類のプログラムにしています。信号の解析にはパルス幅を見て制御する必要があります。そのため、パルス幅の測定にはTIM2を1μ秒ごとにカウントアップするようにプリスケーラを設定して、そのままタイマーのカウンタを使い、パルス幅を測定し、信号処理をしています
赤外線リモコン受信プログラム
#include "ch32v003fun.h"
#include <stdio.h>
#include <stdlib.h>
#define AEHA_T 425
#define AEHA_MARGIN 75
#define AEHA_BIT_SIZE 48
#define AEHA_DATA_SIZE 6
#define SONY_T 600
#define SONY_MARGIN 40
#define SONY_BIT_SIZE 20
#define SONY_DATA_SIZE 4
// リモコン用
typedef enum {
recv_none,
recv_ok,
recv_err,
recv_repeat
} RcvCode;
// リモコンフォーマットタイプ
typedef enum {
NEC,
AEHA,
SONY
} IrFormat;
// Sony用赤外線リモコン受信データ構造体
struct sony_ir_data {
unsigned char data;
unsigned short address;
};
// 初期化関数初期化関数
void init(){
// Enable GPIOA,TIM2
RCC->APB2PCENR |= (RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC );
RCC->APB1PCENR |= (RCC_APB1Periph_TIM2);
// PA1 is 10MHz
funPinMode(PA2, FUN_OUTPUT);
funPinMode(PC1, FUN_INPUT);
funDigitalWrite(PA2, FUN_HIGH);
//TIM2 フリーカウンタ
TIM2->PSC = 48 - 1; //TIM2プリスケーラ 1count/us
TIM2->CTLR1 |= TIM_CEN; //TIM2カウント有効(CEN)
}
// Sonyフォーマット
RcvCode SonyIrChek(struct sony_ir_data *irData)
{
unsigned int t;
unsigned char i;
unsigned char bit;
unsigned char data[SONY_BIT_SIZE];
/* リーダ部チェック */
if(funDigitalRead(PC1) == FUN_LOW) {
return recv_none;
}
/* リーダ部確認 */
/* High部分を確認 */
TIM2->CNT = 0;
while(funDigitalRead(PC1) == FUN_HIGH);
t = TIM2->CNT;
TIM2->CNT = 0;
if((t < (4 * (SONY_T - SONY_MARGIN))) || (t > (4 * (SONY_T + SONY_MARGIN)))){
return recv_err;
}
//データ格納
for(bit = 0; bit < SONY_BIT_SIZE ;bit++){
while(funDigitalRead(PC1) == FUN_LOW);
if(TIM2->CNT > 900) break;
while(funDigitalRead(PC1) == FUN_HIGH);
t = TIM2->CNT;
TIM2->CNT = 0;
if((t >= (2 * (SONY_T - SONY_MARGIN))) && (t <= (2 * (SONY_T + SONY_MARGIN)))){
data[bit] = 0x00;
} else {
data[bit] = 0x01;
}
}
// データ復元
// データ部
irData->data = 0x00;
for(i = 0;i < 7; i++){
if(data[i]) {
irData->data |= (1<<i);
} else {
irData->data &= ~(1<<i);
}
}
// アドレス部
irData->address = 0x0000;
for(i = 7;i < bit; i++){
if(data[i]) {
irData->address |= (1<<(i-7));
} else {
irData->address &= ~(1<<(i-7));
}
}
return recv_ok;
}
// 家電協会フォーマット
RcvCode AehaIrChek(unsigned char *irData)
{
unsigned int t;
unsigned char j;
unsigned char bit;
unsigned char data[AEHA_BIT_SIZE];
/* リーダ部チェック */
if(funDigitalRead(PC1) == FUN_LOW) {
return recv_none;
}
/* リーダ部確認 */
/* High部分を確認 */
TIM2->CNT = 0;
while(funDigitalRead(PC1) == FUN_HIGH);
t = TIM2->CNT;
TIM2->CNT = 0;
if((t < (8 * (AEHA_T - AEHA_MARGIN))) || (t > (8 * (AEHA_T + AEHA_MARGIN)))){
return recv_err;
}
/* LOW部分を確認 */
while(funDigitalRead(PC1) == FUN_LOW);
t = TIM2->CNT;
TIM2->CNT = 0;
if((t >= (4 * (AEHA_T - AEHA_MARGIN))) ) {
if (t >= (8 * (AEHA_T - AEHA_MARGIN)) && (t <= (8 * (AEHA_T + AEHA_MARGIN)))) {
return recv_repeat;
}
} else {
return recv_err;
}
//データ格納
for(bit = 0; bit < AEHA_BIT_SIZE ;bit++){
while(funDigitalRead(PC1) == FUN_HIGH);
while(funDigitalRead(PC1) == FUN_LOW);
t = TIM2->CNT;
TIM2->CNT = 0;
if((t < (2 * (AEHA_T - AEHA_MARGIN))) && (t > (4 * (AEHA_T + AEHA_MARGIN)))){
return recv_err;
} else {
if((t >= (2 * (AEHA_T - AEHA_MARGIN))) && (t <= (2 * (AEHA_T + AEHA_MARGIN)))){
data[bit] = 0x00;
} else {
data[bit] = 0x01;
}
}
}
// データ復元
for(j = 0; j < AEHA_DATA_SIZE; j++){
*irData = 0x00;
for(bit = 0; bit < 8; bit++){
if(data[bit+(j * 8)]) {
*irData |= (1<<bit);
} else {
*irData &= ~(1<<bit);
}
}
irData++;
}
return recv_ok;
}
// AEHA 受信処理プログラム
void aeha_ir_recive()
{
RcvCode pat;
unsigned char ahea_data[AEHA_DATA_SIZE];
unsigned char i;
pat = AehaIrChek(ahea_data);
if(pat == recv_ok) {
printf("RCV OK!\n");
printf("Recive Data: ");
for(i=0;i<AEHA_DATA_SIZE;i++){
printf("0x%02x ",ahea_data[i]);
}
printf("\n");
} else if (pat == recv_repeat) {
printf("RCV REPEAT \n");
}
if (ahea_data[4] == 0x01) {
funDigitalWrite(PA2, FUN_LOW);
} else if (ahea_data[4] == 0x02) {
funDigitalWrite(PA2, FUN_HIGH);
}
}
// SONY 受信処理プログラム
void sony_ir_recive()
{
RcvCode pat;
struct sony_ir_data irdata;
pat = SonyIrChek(&irdata);
if(pat == recv_ok) {
printf("RCV OK!\n");
printf("Data Code = %0x\n",irdata.data);
printf("Address Code = %0x\n",irdata.address);
} /* else {
printf("RCV Error... \n");
} */
}
int main(void) {
IrFormat format;
// システム初期化 -> 必ずいる!
SystemInit();
printf("systemclock=%d \n",FUNCONF_SYSTEM_CORE_CLOCK);
// SysTick初期設定(1ms割込設定)
init();
printf("IR RECiVER TEST\n");
format = AEHA;
while (1) {
switch (format) {
case NEC: break;
case AEHA: aeha_ir_recive(); break;
case SONY: sony_ir_recive(); break;
default: break;
}
}
}
プログラムを動作させ、リモコンの信号を取り出しています。実験では家にあったピクセラのPRODIAのリモコンとSONYのBIRAVIAのリモコンでテストしました。下の画面はPRODIAのリモコンの信号を出力したもので、PRODIAのリモコンは家電協フォーマトだったので6バイトのデータとなり、信号を確認したところ、5バイト目がボタンのコードぽいのでそこを見て、リモコンの[1]を押したら、LEDを点灯、リモコンの[2]を押したら、LEDを消灯させるプログラムにしています。SONYの確認プログラムはリモコンのコードを表示するプログラムにしています。
最後に
今回はCH32Vで赤外線リモコンの受信プログラムを作成しました。CH32Vは秋月電子の中で買える格安マイコンですが、高機能なので多くのことに使えると思います。私も、最近使って始めたばかりなので、もう少しチャレンジして取り組みを発信して、投稿を見ている皆さんの少しでも参加になればと思います。
投稿者の人気記事
-
keiske-hongyo
さんが
2025/01/25
に
編集
をしました。
(メッセージ: 初版)
-
keiske-hongyo
さんが
2025/01/25
に
編集
をしました。
ログインしてコメントを投稿する