目次
前
次
制御処理再検討
MCR_VCの場合、画像処理関係のプログラムやハードウエアは
カメラやセンサー専用とする、2プロセッサ方式が一般的。
1プロセッサで画像処理と移動制御を扱う場合、移動に
関する処理は、プロセッサ内蔵モジュールを使わない方
が簡単になります。
移動処理をプロセッサではなく、簡単なハードウエアで実現
することを再度検討してみました。
DCモータ制御
DCモータは、回転速度と回転方向を制御します。
回転方向は、Hブリッジ回路を利用。
Hブリッジ回路にあるバイポーラトランジスタかMOSFET
のベースかゲートに、DUTY比の異なるパルスを与えれば
平均電力が変化して回転速度が変わります。
DUTY比の可変には、プロセッサ内部のタイマー/カウンタ
を利用するのがデジタル制御では、定番です。
比較するカウント値を変更することで、DUTY比を可変します。
アナログ回路では、3角波を生成して、コンパレータに入力し
比較電圧を変えれば、出力電圧のDUTY比を変えられます。
OPアンプを利用した発振器とコンパレータで、次の回路を
構成すれば、DUTY比を可変にできます。
reference(リファレンス電圧)は、次のR-2Rのラダー型
D/Aコンバータを利用すれば、8ビットでも12ビットでも
ある程度の精度で電圧を可変にできます。
回転方向を可変にするには、トランジスタあるいはMOSFETの
ベースかゲートにパルスを出力するかしないかだけの回路を
付加して対応できます。
ダイオードを利用したAND回路で実現できます。
左右にDCモータがある時、必要な部品を見積もってみます。
- 発振器用OPアンプ(2回路入り)x1
- 発振器用抵抗(75kΩ)x2
- 発振器用フィードバック抵抗(220kΩ)x1
- 発振器用キャパシタ(0.1uF)x1
- 8ビットレジスタ74HC574x2
- D/Aコンバータ制御用OPアンプ(2回路入り)x2
- AND回路用ダイオードx8
- AND回路用抵抗(1kΩ)x4
- NPNパワートランジスタx4
- PNPパワートランジスタx4
- ベース用抵抗(1kΩ)x8
- D/Aコンバータ用抵抗(1kΩ)x14
- D/Aコンバータ用抵抗(2kΩ)x18
- ノイズ吸収用キャパシタ(0.1uF)x2
- 2ピンコネクタx2
表面実装用部品を利用すれば、100mmx100mmの面積に
収容できるサイズになります。
制御のシーケンスは、以下でよいでしょう。
- パルス出力を停止(2つのenableに0を指定)
- D/Aコンバータ用レジスタにDUTY比値設定
- 回転方向設定(どちらかのenableに1を指定)
D/Aコンバータ用レジスタが8ビットが2つあり
方向設定enableが4つあるので、マイコンからは
8ビットのI/Oアドレスが2つあれば充分。
サーボモータ制御
サーボモータは、20msごとに1msから2msの幅のパルスを
出力すればよい制御できます。
1ms幅のパルス出力は固定なので、0.1ms幅の'1'を
0から10個出力します。
10ビットレジスタにパターンを設定し、データセレクタ
で、0ビットから9ビットの各値を出力します。
4ビットのレジスタを3個用意し、パラレル入力シフト出力
の8ビットシフトレジスタを使えば実現できます。
0.1ms幅(10kHz)でカウンタを0から199まで回します。
これはフリーランカウンタで充分です。
カウンタの値が0から20で、サーボモータにパルスを出力
し、値を変更したいときは、21から199の間に10ビット
を3回に分けて与えます。
カウンタ値が21から199であれば、10ビット値変更を
許可するイネーブル信号を出して対応します。
カウンタの値が0から9で、'1'を出力し、10から20までは
セレクタの値を出力、21から199までは'0'を出力する
制御回路を用意します。
カウンタは12ビットカウンタの4040を利用し、カウンタ
出力を加工して対応します。
条件が複雑なときには、ROMをデコーダとして利用します。
カウンタは0から199を繰り返すので、199になったときに
4040をリセットする信号を出力します。
A7からA0で199になったときに、出力D5を1にし、それ以外は
0になるようにパターンを設定します。
カウンタが0から9と21から199では、nSTBを1にして
セレクタを動かさないようにします。
0から9 出力D4=D3=1
21から199 出力D4=D3=1
カウンタが10から20までは、nSTB1、nSTB2とSEL2からSEL0
を組み合わせて、データセレクタを動かします。
組み合わせをリストすると、以下。
nSTB2 nSTB1 SEL2 SEL1 SEL0
10 1 0 0 0 0
11 1 0 0 0 1
12 1 0 0 1 0
13 1 0 0 1 1
14 1 0 1 0 0
15 1 0 1 0 1
16 1 0 1 1 0
17 1 0 1 1 1
18 0 1 0 0 0
19 0 1 0 0 1
20 0 1 0 1 0
D4 D3 D2 D1 D0
ROMに書込むビットパターンは、プログラムで生成します。
Tcl/Tkで記述すると、以下。
for {set i 0} {$i < 200} {incr i} {
# default
set xdat 255
# reset
if { $i == 199 } {
set xdat [expr $xdat - 32]
}
# 10 -> 20
if { $i < 21 && 9 < $i } {
if { $i < 18 } {
set xdat [expr 16 + $i - 10]
} else {
set xdat [expr 8 + $i - 18]
}
}
# calculate
set tmp [format "%02X %02X" $i $xdat]
# show
puts $tmp
}
実際にROMに書込むには、手持ちのROMライターを利用します。
ROMがなければ、CPLDで代用します。
CPLDならXC9536、XC9572のようなマクロセル数が少ないデバイス
で対応できます。
DCモータ制御2
DCモータを1方向だけに回転する場合、DUTY比だけを設定し
波形出力を小型マイコンに任せる方法もあります。
8ビットレジスタにDUTY比(0から100%)を設定しておくと
PICがシフトレジスタにトリガーを与えて、DUTY比をコピーし
シフトクロックで取込みます。
PICのファームウエアは、以下。
#define OFF 0
#define ON OFF+1
#define PCNTMAX 100
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
typedef union {
struct {
unsigned B7:1;
unsigned B6:1;
unsigned B5:1;
unsigned B4:1;
unsigned B3:1;
unsigned B2:1;
unsigned B1:1;
unsigned B0:1;
} BIT ;
unsigned char DR ;
} FLAGSP ;
volatile FLAGSP xflags ;
#define TMP xflags.BIT.B0
#define TFLAG xflags.BIT.B1
#define DIN PORTA.F5
#define SCLK PORTA.F4
#define PULSEOUT PORTA.F0
#define CNTBEGIN 246
volatile UBYTE pcnt ;
volatile UBYTE qcnt ;
volatile UBYTE xcnt ;
volatile UBYTE ycnt ;
volatile UBYTE zcnt ;
/* function prototype */
void init_usr(void);
void send_clk();
/* interrupt handler */
void interrupt(void)
{
/* timer0 interrupt */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = CNTBEGIN ;
/* impress */
TMP = OFF ;
if ( pcnt < xcnt ) { TMP = ON ; }
PULSEOUT = TMP ;
/* increment */
pcnt++ ;
/* judge */
if ( xcnt == PCNTMAX ) {
/* clear counter */
pcnt = 0 ;
/* reload */
xcnt = zcnt ;
}
/* set event flag */
TFLAG = ON ;
}
}
void main(void)
{
/* initialize */
init_usr() ;
/* endless loop */
while ( ON ) {
/* direction */
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* copy counter value */
qcnt = pcnt ;
/* clear */
if ( qcnt == 7 ) { ycnt = 0 ; }
/* get data */
if ( 8 <= qcnt && qcnt < 16 ) {
/* get shift data */
ycnt |= (DIN << (qcnt & 7));
/* send shift pulse */
send_clk();
}
/* generate code */
if ( qcnt == 16 ) { zcnt = ycnt ; }
}
}
}
/* define function body */
void init_usr(void)
{
/* select 16MHz */
OSCCON = (0x0f << 3) | 0x03 ;
/* disable A/D converter */
ADCON0.ADON = OFF ;
ADCON2 = 0 ;
/* disable D/A converter */
DACCON0.DACEN = OFF ;
/* disable compare module */
CM1CON0.C1ON = OFF ;
CM1CON0.C1OE = OFF ;
/* I/O state */
LATA = 0 ;
/* I/O directions */
TRISA = 0x20 ; /* bit0,4 as output , others as input */
/* pull-up */
WPUA = 0x20 ;
/* initialize Timer 0 */
{
/*
16MHz/4 = 4MHz (BUS clock) -> 4MHz/4 = 1MHz prescaler = 1:4
*/
OPTION_REG = 0x01 ;
/* 256 - 10 = 246 */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.TMR0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* clear flags */
xflags.DR = 0 ;
/* initialize clock logic level */
SCLK = ON ;
/* initialize variables */
xcnt = 0 ;
ycnt = 0 ;
zcnt = 0 ;
pcnt = 0 ;
}
void send_clk()
{
/* SCLK : L */
SCLK = OFF ;
/* SCLK : H */
SCLK = ON ;
}
DCモータのDUTY比を与える部分を、サーボモータの20msに一度
1msから2msのパルスを出力するよう変更すれば、サーボモータ
にも対応できます。
#define OFF 0
#define ON OFF+1
#define MASKFF 0xff
#define PCNTMAX 2000
typedef unsigned char UBYTE ;
typedef unsigned short UWORD ;
typedef union {
struct {
unsigned B7:1;
unsigned B6:1;
unsigned B5:1;
unsigned B4:1;
unsigned B3:1;
unsigned B2:1;
unsigned B1:1;
unsigned B0:1;
} BIT ;
unsigned char DR ;
} FLAGSP ;
volatile FLAGSP xflags ;
#define TMP xflags.BIT.B0
#define TFLAG xflags.BIT.B1
#define DIN PORTA.F5
#define SCLK PORTA.F4
#define PULSEOUT PORTA.F0
#define CNTBEGIN 246
volatile UWORD pcnt ;
volatile UBYTE qcnt ;
volatile UBYTE xcnt ;
volatile UBYTE ycnt ;
volatile UBYTE zcnt ;
/* function prototype */
void init_usr(void);
void send_clk(void);
/* interrupt handler */
void interrupt(void)
{
/* timer0 interrupt */
if ( INTCON.T0IF == ON ) {
/* clear flag */
INTCON.T0IF = OFF ;
/* initialize */
TMR0 = CNTBEGIN ;
/* impress */
TMP = OFF ;
if ( pcnt < xcnt ) { TMP = ON ; }
PULSEOUT = TMP ;
/* increment */
pcnt++ ;
/* judge */
if ( xcnt == PCNTMAX ) {
/* clear counter */
pcnt = 0 ;
/* reload */
xcnt = zcnt ;
}
/* set event flag */
TFLAG = ON ;
}
}
void main(void)
{
/* initialize */
init_usr() ;
/* endless loop */
while ( ON ) {
/* direction */
if ( TFLAG == ON ) {
/* clear flag */
TFLAG = OFF ;
/* copy counter value */
qcnt = pcnt & MASKFF ;
/* clear */
if ( qcnt == 7 ) { ycnt = 0 ; }
/* get data */
if ( 8 <= qcnt && qcnt < 16 ) {
/* get shift data */
ycnt |= (DIN << (qcnt & 7));
/* send clock */
send_clk();
}
/* generate code */
if ( qcnt == 16 ) { zcnt = ycnt ; }
}
}
}
/* define function body */
void init_usr(void)
{
/* select 16MHz */
OSCCON = (0x0f << 3) | 0x03 ;
/* disable A/D converter */
ADCON0.ADON = OFF ;
ADCON2 = 0 ;
/* disable D/A converter */
DACCON0.DACEN = OFF ;
/* disable compare module */
CM1CON0.C1ON = OFF ;
CM1CON0.C1OE = OFF ;
/* I/O state */
LATA = 0 ;
/* I/O directions */
TRISA = 0x28 ; /* bit0,4 as output , others as input */
/* pull-up */
WPUA = 0x20 ;
/* initialize Timer 0 */
{
/*
16MHz/4 = 4MHz (BUS clock) -> 4MHz/4 = 1MHz prescaler = 1:4
*/
OPTION_REG = 0x01 ;
/* 256 - 10 = 246 */
TMR0 = CNTBEGIN ;
/* enable timer0 overflow interrupt */
INTCON.TMR0IE = ON ;
}
/* enable general interrupt */
INTCON.GIE = ON ;
/* clear flags */
xflags.DR = 0 ;
/* set clock logic level */
SCLK = ON ;
/* initialize variables */
xcnt = 150 ;
ycnt = 0 ;
zcnt = 150 ;
pcnt = 0 ;
}
void send_clk(void)
{
/* SLCK : L */
SCLK = OFF ;
/* SLCK : H */
SCLK = ON ;
}
DCモータ、サーボモータともにDUTY比、パルス幅を担当する
カウンタの値が9から15の間に、パラメータを取得します。
カウンタの値が16のときに、内部レジスタに転送します。
100kHzでカウンタを動かすので、DCモータの場合1周期の間に
4回、サーボモータの場合1周期の間に100回、パラメータを
更新します。
74HC165に上位プロセッサが、非同期でパラメータを設定した
としても、PICは1周期中に何度もパラメータを更新している
ので、モータの挙動がおかしくなる確率は小さくなります。
目次
前
次