製作途中のブログ作成しました。
一通りの制御出来る様になったら、後日記事をまとめます。
概要
タミヤカムロボットはそれ単体でもよく出来た玩具ですが、後からマイコンボードやサーボモータを追加出来る様な構造になっています。
大分昔にArduino Nanoで改造したのですが、最近覚え始めたPICで改めて改造し直しました。
Arduino Nanoですと時折モーターの影響による電圧降下で暴走したので、Arudinoよりも低電圧で動作可能なPICの方が向いていると思います。
あと、PICの方が省スペースで設置出来るメリットがありますしね。
*備忘録も兼ねてるので、記事の内容は随時更新していきます。
取敢えずMCCの設定とプログラムを書いて、一通り書き終わったら回路図も作成していきたいです。
DRV8830モータードライバ
部品選定
パワーMOSFETでHブリッジ回路組んで、モーター制御も検討しましたが、スペースの問題とI2Cで制御できるドライブがあったので秋月電子で購入したAE-DRV8830を選定。
動作電圧2.75V~なのも魅力。
MCC設定
プログラム
I2Cモジュールを選択して設定すると新たに”i2c1_master_example.c"ファイルが作成されるので、その中の”I2C1_Write1ByteRegister(i2c1_address_t address, uint8_t reg, uint8_t data)”関数を使用する。
#include "mcc_generated_files/examples/i2c1_master_example.h"を宣言しないと、I2Cの関数が使用できないので宣言する。
スピード(電圧)は0x06-0x3F(0.48V-5.06V)まで設定出来るけど、0x06では動かない。
#include "mcc_generated_files/mcc.h"
#include "mcc_generated_files/examples/i2c1_master_example.h"
void Forward(uint8_t Speed) {
Speed=Speed<<2;
Speed=Speed+0b10;
I2C1_Write1ByteRegister(0x63, 0x00, Speed);
I2C1_Write1ByteRegister(0x64, 0x00, Speed);
__delay_ms(500);
}
void Back(uint8_t Speed) {
Speed=Speed<<2;
Speed=Speed+0b01;
I2C1_Write1ByteRegister(0x63, 0x00, Speed);
I2C1_Write1ByteRegister(0x64, 0x00, Speed);
__delay_ms(500);
}
void main(void) {
SYSTEM_Initialize();
while (1) {
//Speed 0x06-0x3F
Forward(0x16);
__delay_ms(2000);
Back(0x2F);
__delay_ms(2000);
}
}
動画
左右のモーターは可変出来るので、曲がるスピードを変えたり振り返ることも出来る。
超音波センサ
HC-SR04
トリガで超音波を発振して、反射した音波を受けるとエコー端子がONする。
詳細はスイッチサイエンスなどからデータシート確認出来るのでそちらを参照して下さい。
計算方法も乗っています。
MCC設定
プログラム
平均化しなくても、それなりに距離が出ていたので速度重視で平均化しなかった。
変数numの値を変更することで平均化回数を設定。
#include "mcc_generated_files/mcc.h"
#include "stdio.h"
float Distance() {
// uint16_t distance;
int num=1;
float distance1;
float distance2;
for (int i = 0; i < num; ++i) {
RA5 = 0;
__delay_us(2);
RA5 = 1;
__delay_us(10);
RA5 = 0;
uint16_t Time1 = TMR1_ReadTimer();
while (RC0 == 0 || TMR1_ReadTimer() - Time1 > 65500);
Time1 = TMR1_ReadTimer();
while (RC0 == 1 || TMR1_ReadTimer() - Time1 > 65500);
Time1 = TMR1_ReadTimer() - Time1;
// distance=Time1;
distance1 = Time1 / 8 * (331.5 + 0.61 * 20) *100 / 1000000;
distance2 = distance2 + distance1;
__delay_ms(60);
}
distance2 = distance2 / num;
return (distance2);
}
void main(void) {
SYSTEM_Initialize();
while (1) {
printf("Distance_%f\n", Distance());
}
}
動画
小さくて見にくいですが、障害物を検知してモニタに表示されている数値が変化しています。
照度センサ
照度センサーと100kΩの抵抗で分圧させた電圧をPICで読み取る。
MCC設定
プログラム
暗くなると目が光り、明るくなると消灯する。
#include "mcc_generated_files/mcc.h"
void Lighton(void) {
RA4 = 1;
RA3 = 1;
}
void Lightoff(void) {
RA4 = 0;
RA3 = 0;
}
void main(void) {
SYSTEM_Initialize();
while (1) {
int Vale=ADCC_GetSingleConversion(channel_ANB3);
if(Vale<100){
Lighton();
}else if(Vale>=200){
Lightoff();
}
}
}
動画
サーボモータ
QKY66
形状はSG90と互換性があります。Amazonで20個/2000円くらいで購入。
カムロボットの両腕に取付けました。
0.5ms~2.5msのパルス信号を送ると0~180°回転します。
プログラム
PICのサーボ制御はタイマー2とCCPモジュール(PWM制御)で可能ですが、貧弱な電源だと電圧降下が起きて複数軸のサーボは制御出来ません。
2軸くらいなら単三乾電池4本でもOKかもしれませんが、タンクモーターにも電源取られるので極力省エネで行きます。
今回はタイマー制御で0.5ms~2.5msのパルス信号を送り、両腕の上げ下げを行いました。
#include "mcc_generated_files/mcc.h"
int ServoPOSI1 = 58;
int ServoPOSI2 = 58;
int ServoPOSI3 = 58;
int ServoPOSI4 = 58;
void ServoQuickMove(int Angle1, int Angle2) {
int ServoPOSI1 = Angle1;
int ServoPOSI2 = Angle2;
for (int i = 0; i < 1; i++) {
RA6 = 1;
RA3 = 1;
for (int j = 0; j < ServoPOSI1; j++) {
__delay_us(10);
}
RA6 = 0;
RA3 = 0;
for (int k = 0; k < 500 - ServoPOSI1; k++) {
__delay_us(10);
}
}
__delay_ms(1);
for (int i = 0; i < 1; i++) {
RA7 = 1;
for (int j = 0; j < ServoPOSI2; j++) {
__delay_us(10);
}
RA7 = 0;
for (int k = 0; k < 500 - ServoPOSI2; k++) {
__delay_us(10);
}
}
__delay_ms(1);
}
void ServoMove(int delayTIME, int Angle1, int Angle2) {
while (ServoPOSI1 != Angle1 || ServoPOSI2 != Angle2) {
for (int i = 0; i < delayTIME; i++) {
__delay_us(1);
}
// 0,5ms:0°_1.45ms:90°_2.4ms:180°
// printf("move");
if (ServoPOSI1 < Angle1) {
++ServoPOSI1;
} else if (ServoPOSI1 > Angle1) {
--ServoPOSI1;
} else {
ServoPOSI1 = Angle1;
}
if (ServoPOSI2 < Angle2) {
++ServoPOSI2;
} else if (ServoPOSI2 > Angle2) {
--ServoPOSI2;
} else {
ServoPOSI2 = Angle2;
}
for (int k = 0; k < 5; k++) {
for (int i = 0; i < 1; i++) {
RA6 = 1;
for (int j = 0; j < ServoPOSI1; j++) {
__delay_us(10);
}
RA6 = 0;
for (int k = 0; k < 100 - ServoPOSI1; k++) {
__delay_us(10);
}
}
__delay_us(1);
for (int i = 0; i < 1; i++) {
RA7 = 1;
for (int j = 0; j < ServoPOSI2; j++) {
__delay_us(10);
}
RA7 = 0;
for (int k = 0; k < 100 - ServoPOSI2; k++) {
__delay_us(10);
}
}
__delay_us(1);
}
}
}
void Banzai(void) {
ServoMove(10, 60, 30);
__delay_ms(1000);
ServoMove(10, 30, 60);
__delay_ms(1000);
}
void main(void) {
SYSTEM_Initialize();
while (1) {
Banzai();
}
}
void ServoQuickMove(int Angle1, int Angle2)
void ServoMove(int delayTIME, int Angle1, int Angle2)
サーボを制御する関数は二通り制作しましたが、今回は速度を遅くするServoMoveを使用しました。
動画
@[youtube] (https://youtu.be/vz9aO5RjBZ4)
フォトリフレクタ
LBR-127HLD
秋月電子で購入、100~300Ω程度の抵抗を入れて赤外線LEDを点灯。
電源に直結させていますが、消費電力の事を考えると、トランジスタ及びPWM制御で点灯したいので
何れ回路修正する予定。
MMC設定
プログラム
取敢えずIOチェック用のプログラムを作成。
シリアルモニタで確認出来る様にする。センサー位置の並びをあまり考えずに配線したので、ソフト上で並びを整理しておきます。
ロボットの左手側から#1~5番とする。
内部プルアップ抵抗を使用して、どのセンサーにも入力が無ければ"31"。
すべてのセンサーに入力があれば"0"となる。
#include "mcc_generated_files/mcc.h"
#include "stdio.h"
int PhotoRef(void) {
int PhotoVal = 0;
if (RA1 == 1) {
PhotoVal =PhotoVal+ 0b010000;
}
if (RB7 == 1) {
PhotoVal =PhotoVal+ 0b01000;
}
if (RB5 == 1) {
PhotoVal =PhotoVal+ 0b0100;
}
if (RA0 == 1) {
PhotoVal = PhotoVal + 0b10;
}
if (RB6 == 1) {
PhotoVal =PhotoVal+ 0b01;
}
return PhotoVal;
}
void main(void) {
SYSTEM_Initialize();
while (1) {
//0~31の変化
printf("%d\n", PhotoRef());
}
}
赤外線受信センサ
OSRB38C9AA
秋月電子で購入。2ヶ/100円です。
38kHzの赤外線の波長を受信すると信号線がON→OFFに変化する。(信号が無い時は常時ON)
赤外線はTVやエアコンなどに使用されていて、メーカー毎にコード規格がある。
今回は自分で赤外線送信機まで制作したいので、独自規格で制作する事にする。
プログラム
赤外線受信モジュール信号をRA2ピンに接続。
まずは単純にON,OFF時間を計測プログラムを制作。その後、信号パターンをコードかするプログラムを制作する。
#include "mcc_generated_files/mcc.h"
#include "stdio.h"
#define Mode 1
int num = 0;
unsigned int IRvaltime[100];
unsigned int IRonvaletime[100];
unsigned int IRoffvaletime[100];
int point = 0;
unsigned int TMR0vale = 0;
unsigned int TMR1vale = 0;
unsigned int TMR1onvale = 0;
unsigned int TMR1offvale = 0;
#if Mode==0
void main(void) {
SYSTEM_Initialize();
while (1) {
}
}
#elif Mode==1 //OnOff時間計測
void main(void) {
SYSTEM_Initialize();
while (1) {
TMR1vale=TMR3_ReadTimer();
while (RA2 == 0 && point == 0) { //信号ON時間計測開始
TMR1onvale = TMR3_ReadTimer() - TMR1vale;
}
while (RA2 == 0);
TMR1onvale = TMR3_ReadTimer() - TMR1vale;
while (RA2 == 1 && point == 0) { //信号ON時間登録
IRonvaletime[num] = TMR1onvale;
TMR1vale = TMR3_ReadTimer();
point = 1;
}
while (RA2 == 1 && point == 1) { //信号OFF時間計測
TMR1offvale = TMR3_ReadTimer() - TMR1vale;
if (TMR1offvale > 60000) { //信号OFF時間30ms以上の時計測中止
point = 2;
}
}
while (RA2 == 0 && point == 1) { //信号OFF時間登録、point0へ
IRoffvaletime[num] = TMR1offvale;
TMR1vale = TMR3_ReadTimer();
num = num + 1;
point = 0;
}
while (RA2 == 1 && point == 2) { //計測時間の表示と初期化
for (int i = 0; i < num; i++) {
printf("[%don]%u\n", i, IRonvaletime[i]);
printf("[%doff]%u\n", i, IRoffvaletime[i]);
}
num = 0;
point = 0;
}
}
}
#endif
TVリモコンの信号は読み込んでいた。
後日、実際の波長とプログラムで表示された数値をオシロスコープで比較する。
良ければこのプログラムを元にコード読み取りのプログラムを制作する。
三菱TVリモコンの電源ボタン波形写真
受信器側で受信したON,OFF時間の結果
赤外線送信機の制作
送信は8byteのデータで送ることにする。
赤外線LEDがON→OFFしてから次に点灯するまで
__delay_us(300)としたものを「0」
__delay_us(1000)としたものを「1」とする。
##回路図
製作中
##プログラム
#include "mcc_generated_files/mcc.h"
#include <stdio.h>
#define Mode 0
uint8_t IRData = 0b00000000;
bool IRplsPattern;
/*
* IR_
* LED_
* LIGHT_GetValue()
* FORW_
* RIGHT_
* LEFT_
* BACK_
*/
void IR0(void) {
for (int i = 0; i < 10; i++) {
IR_SetHigh();
// LED_SetHigh();
__delay_us(13);
IR_SetLow();
LED_SetLow();
__delay_us(13);
}
__delay_us(300);
}
void IR1(void) {
for (int i = 0; i < 10; i++) {
IR_SetHigh();
// LED_SetHigh();
__delay_us(13);
IR_SetLow();
LED_SetLow();
__delay_us(13);
}
__delay_us(1000);
}
void IRblink(void) {
for (int k = 0; k < 2; k++) {
for (int i = 0; i < 8; i++) {
IRplsPattern = IRData >> i & 1;
if (IRplsPattern == 0) {
IR0();
} else {
IR1();
}
}
//STOPbit
for (int i = 0; i < 10; i++) {
IR_SetHigh();
__delay_us(13);
IR_SetLow();
LED_SetLow();
__delay_us(13);
}
__delay_ms(10);
}
}
void main(void) {
SYSTEM_Initialize();
TMR1_WriteTimer(0);
while (1) {
if (LIGHT_GetValue() == 1 && FORW_GetValue() == 1 && RIGHT_GetValue() == 1
&& RC4 == 1 && BACK_GetValue() == 1) {
IRData = 0b00000000;
}
if (LIGHT_GetValue() == 0) {
IRData = 0b00000010;
LED_SetHigh();
}
if (FORW_GetValue() == 0 && RC4 == 1
&& RIGHT_GetValue() == 1 && BACK_GetValue() == 1) {
IRData = 0b00100000;
}
if (FORW_GetValue() == 0 && RC4 == 0
&& RIGHT_GetValue() == 1 && BACK_GetValue() == 1) {
IRData = 0b00101000;
}
if (FORW_GetValue() == 0 && RC4 == 1
&& RIGHT_GetValue() == 0 && BACK_GetValue() == 1) {
IRData = 0b00100100;
}
if (FORW_GetValue() == 1 && RC4 == 1
&& RIGHT_GetValue() == 1 && BACK_GetValue() == 0) {
IRData = 0b00010000;
}
if (FORW_GetValue() == 1 && RC4 == 0
&& RIGHT_GetValue() == 1 && BACK_GetValue() == 0) {
IRData = 0b00011000;
}
if (FORW_GetValue() == 1 && RC4 == 1
&& RIGHT_GetValue() == 0 && BACK_GetValue() == 0) {
IRData = 0b00010100;
}
if (FORW_GetValue() == 1 && RC4 == 0
&& RIGHT_GetValue() == 1 && BACK_GetValue() == 1) {
IRData = 0b00001000;
}
if (FORW_GetValue() == 1 && RC4 == 1
&& RIGHT_GetValue() == 0 && BACK_GetValue() == 1) {
IRData = 0b00000100;
}
IRblink();
__delay_ms(100);
}
}
送信側と受信器側の波形
送信器側LED波形写真
受信器側モジュールの波形写真
DFplayer mini
製作中
MCC設定
製作中
プログラム
制作中
投稿者の人気記事
-
masayasan
さんが
2021/10/15
に
編集
をしました。
(メッセージ: 初版)
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/15
に
編集
をしました。
-
masayasan
さんが
2021/10/16
に
編集
をしました。
-
masayasan
さんが
2021/10/16
に
編集
をしました。
-
masayasan
さんが
2021/10/25
に
編集
をしました。
(メッセージ: ブログリンク先を貼り付け)
-
masayasan
さんが
2021/10/25
に
編集
をしました。
ログインしてコメントを投稿する