PageTopへ戻る
8.列車のスピード(モーターの回転速度)の検出
●次は列車のモーター回転による発電電圧を検出してPCにスピードデータを送信します。
・ブレッドボードにRS232CのインターフェイスICであるMAX232CPEを追加します。これにより、PCにRS232Cで送信が出来ます。
・レール出力を分圧して、ADCの入力ポートに接続します。
・オシロの画面でわかるように、モーターが回転すると、モーター自体から回転速度に応じた電圧が発生し、パルスOFFの時に検出することが出来ます。
・従って、パルスONの直前(IntCounter = 0x1FF)にこの発電電圧を読み込みます。
・dsPICのADC入力の0〜5Vになるように抵抗で分圧して入力ポートPB9に接続します。
【dsPICのコード】
// PCリモートによるモーター制御 // RS232C スピード送信 #include
#include
#include
#include
//16bitのINT型の上位H byte、下位L byte読み込み用共用体 union byte_access { unsigned int INT; // Int Access struct{ // byte Access unsigned char L; unsigned char H; }BYTE; }; // A/D control register unsigned int _ADCON1; unsigned int _ADCON2; unsigned int _ADCON3; unsigned int _ADCHS; unsigned int _ADPCFG; unsigned int _ADCSSL ; // UART control register unsigned int _U1BRG; unsigned int _U1MODE; unsigned int _U1STA; #define Fcy 7370000*4 #define BaudRate 115200 unsigned int int_counter; //インタラプトカウンタ unsigned int speed; //スピードパルス幅 unsigned int speed_set; //スピード設定 unsigned char direction; //方向 union byte_access speed_ret; //読み取りスピード int main(void) { //dsPIC基板のブートローダーの制約によりAIVTを使用 INTCON2bits.ALTIVT=1; // A/D変換モジュールオフ確認 ADCON1bits.ADON=0; // A/D変換モジュール初期化 (PB9をADCに使用) _ADCHS= ADC_CH0_POS_SAMPLEA_AN9 & ADC_CH0_NEG_SAMPLEA_NVREF; SetChanADC12(_ADCHS); // ADC割り込み禁止 ConfigIntADC12(ADC_INT_DISABLE); _ADCON1=ADC_MODULE_ON & ADC_IDLE_CONTINUE & ADC_FORMAT_INTG & ADC_CLK_AUTO & ADC_AUTO_SAMPLING_OFF & ADC_SAMP_OFF; _ADCON2=ADC_VREF_AVDD_AVSS & ADC_SCAN_OFF & ADC_SAMPLES_PER_INT_1 & ADC_ALT_BUF_OFF & ADC_ALT_INPUT_OFF; _ADCON3=ADC_SAMPLE_TIME_1 & ADC_CONV_CLK_SYSTEM & ADC_CONV_CLK_10Tcy; _ADPCFG=ENABLE_AN9_ANA; _ADCSSL=SCAN_NONE; OpenADC12(_ADCON1, _ADCON2, _ADCON3, _ADPCFG, _ADCSSL); //Timer1設定 OpenTimer1(T1_ON & T1_GATE_OFF & T1_PS_1_1 & T1_SYNC_EXT_OFF & T1_SOURCE_INT, 586); ConfigIntTimer1(T1_INT_PRIOR_5 & T1_INT_ON); // UART1モジュールのオフを確認します CloseUART1(); // UART1割り込み禁止 ConfigIntUART1(UART_RX_INT_DIS & UART_TX_INT_DIS); // UART1の初期化 _U1BRG=((Fcy/BaudRate)/16); // パリティなし、データ8bit、ストップ1bit _U1MODE=UART_EN & UART_IDLE_CON & UART_ALTRX_ALTTX & UART_DIS_WAKE & UART_DIS_LOOPBACK & UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_1STOPBIT; _U1STA= UART_INT_TX_BUF_EMPTY & UART_TX_PIN_NORMAL & UART_TX_ENABLE & UART_INT_RX_CHAR & UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR; // Open UART1 OpenUART1(_U1MODE, _U1STA, _U1BRG); _TRISB0=0; //PB0:OUT Portに設定 _TRISB1=0; //PB1:OUT Portに設定 _TRISB9=1; //PB9:ADC入力端子 In Portに設定 while(1) { //RS232Cの受信 //ヘッダー確認 while(U1STAbits.URXDA==0); // wait RX data if(ReadUART1() == 0x55){ while(U1STAbits.URXDA==0); // wait RX data if(ReadUART1() == 0xAA){ //0x55,0xAAが続けてきたら正しいデータとして受信 while(U1STAbits.URXDA==0); // wait RX data speed_set=ReadUART1(); //受信したらspeed_setに設定 speed_set <<= 1; //簡単のため8bitデータを左シフトして9bitにします while(U1STAbits.URXDA==0); // wait RX data direction = ReadUART1(); //受信したら方向に設定 //ヘッダー0x55,0xAAの送信 WriteUART1(0x55); while(BusyUART1()); WriteUART1(0xAA); while(BusyUART1()); //スピード読み取り値の送信 WriteUART1(speed_ret.BYTE.H); while(BusyUART1()); WriteUART1(speed_ret.BYTE.L); while(BusyUART1()); } } } } void __attribute__((__interrupt__, __shadow__, no_auto_psv)) _T1Interrupt(void) { _RB1=1; //割り込み期間オシロ監視用 //タイマーインタラプトを0-0x1ffでカウントします。 if(++int_counter > 0x1ff){ int_counter = 0; } //インタラプトカウンタが0のときspeedパルスON if(int_counter == 0){ speed = speed_set; //speedパルス幅のセット if(direction == 0) _RB0=1; //direction=0で正転 else _RB0=0; //direction=0xFFで逆転 _TRISB0=0; //パルスON } //インタラプトごとにspeedをマイナス1して0になったらパルスOFF if(speed == 0){ //speedが0でパルスOFF _TRISB0=1; } else{ speed--; //speedをマイナス1 } //インタラプトカウンタが0x1FFのときADCでモーターの発電電圧読み込み if(int_counter == 0x1FF){ ADCON1bits.SAMP=1; while(BusyADC12()); speed_ret.INT = ReadADC12(0); } _RB1=0; //割り込み終了監視 _T1IF = 0; //割り込みフラグ解除 }
【PC側のソフト】
・7.PCから両方向制御のソフトにRectangleShapeを追加します。
・また、すべてのコードを削除し、下記のコードを貼り付けます。
Imports System.IO.Ports Public Class Form1 'デリゲートの宣言 Public Delegate Sub RsDelegate() 'データーの受信 Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _ Handles SerialPort1.DataReceived 'データの受信はマルチスレッドで行われる為にデリゲートを使用して 'メインのスレッドでデータ処理の必要があります。 Me.Invoke(New RsDelegate(AddressOf receive_data), New Object() {}) End Sub '受信データー Public Sub receive_data() Dim byteArray(SerialPort1.BytesToRead - 1) As Byte Static GND_Level(9) As Integer Static p As Integer If byteArray.Length <> 4 Then Exit Sub 'ポートのバッファから読み込み SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead) 'ヘッダーチェック If (byteArray(0) <> &H55) Or (byteArray(1) <> &HAA) Then Exit Sub 'ADCの値 Dim ADC As Integer = byteArray(2) * 256 + byteArray(3) Dim speed_max = 500 Dim speed As Integer 'TrackBarが0のとき2.5VGND基準電圧を読み込みます。 If TrackBar1.Value = 0 Then '10回の移動平均をとります。 GND_Level(p) = ADC p += 1 If p > 9 Then p = 0 TrackBar1.Enabled = True '最初の平均が出てからTrackBarをEnableします。 End If End If If TrackBar1.Enabled Then 'スピードメータ− speed = Math.Abs(ADC - GND_Level.Average) RectangleShape2.Height = speed / speed_max * RectangleShape1.Height RectangleShape2.Top = RectangleShape1.Top + RectangleShape1.Height - RectangleShape2.Height End If End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'ポート接続処理 SerialPort1.PortName = ComboBox1.Text SerialPort1.BaudRate = 115200 SerialPort1.Parity = IO.Ports.Parity.None SerialPort1.DataBits = 8 SerialPort1.StopBits = IO.Ports.StopBits.One SerialPort1.ReceivedBytesThreshold = 4 SerialPort1.ReadTimeout = 500 SerialPort1.WriteTimeout = 500 SerialPort1.Open() Timer1.Enabled = True Button1.Enabled = False Button2.Enabled = True ComboBox1.Enabled = False End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'ポート切断処理 Timer1.Enabled = False SerialPort1.Close() ComboBox1.Enabled = True Button1.Enabled = True Button2.Enabled = False TrackBar1.Enabled = False End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Dim Txdata(3) As Byte 'ヘッダーとして&H55,&HAAを送る Txdata(0) = &H55 Txdata(1) = &HAA 'トラックバーの値を送信します。 Txdata(2) = TrackBar1.Value 'speed data '方向CheckBoxの状態を送ります。 If CheckBox1.Checked Then Txdata(3) = &HFF '逆転のとき&HFFを送る Else Txdata(3) = 0 '正転のとき0を送る End If Try SerialPort1.Write(Txdata, 0, 4) '4BYTEデータ送信 Catch ex As Exception 'エラーのとき Button2_Click(Nothing, Nothing) '切断 MsgBox(ex.Message) End Try End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim ports As String() = SerialPort.GetPortNames() If ports.Count = 0 Then MsgBox("シリアルポートが見つかりません。") : End '使用可能なシリアルポートをコンボボックスに登録 ComboBox1.Items.AddRange(ports) ComboBox1.SelectedIndex = 0 Me.Text = "PC Remote" Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D Me.MinimizeBox = False Me.MaximizeBox = False Button1.Text = "接続" Button2.Text = "切断" Button2.Enabled = False '切断ボタンdisable '逆転ボタン用checkboxの設定 CheckBox1.Appearance = Appearance.Button CheckBox1.AutoSize = False CheckBox1.Size = Button1.Size CheckBox1.TextAlign = ContentAlignment.MiddleCenter CheckBox1.Text = "逆転" 'スピードメーターの設定 RectangleShape2.FillStyle = PowerPacks.FillStyle.Solid RectangleShape2.FillColor = Color.Pink RectangleShape2.Left = RectangleShape1.Left RectangleShape2.Width = RectangleShape1.Width RectangleShape2.Height = 0 RectangleShape2.Top = RectangleShape1.Top + RectangleShape1.Height - RectangleShape2.Height TrackBar1.Enabled = False TrackBar1.Maximum = 255 TrackBar1.TickFrequency = 10 Timer1.Interval = 200 'タイマーインターバル200ms End Sub End Class
・ADCで読み込んだ電圧はモーターが動いていない時は2.5Vなので、この時の値を基準にします。
・正転のときは2.5Vより高い電圧が検出され、逆転時は2.5Vより低い電圧が返ってきます。
・従って、返ってきたADC値から基準値を引いて絶対値をとった値をスピードメータの値にします。
・基準値は0xFFFの中間ぐらいの値になり、実際は0x7F0位になります。
・動かすとわかるのですがスピード値はADC値をそのまま使っているのでかなりバタつきます。
(2015/03追加)
・VB2013Expressの場合のコード
Imports System.IO.Ports Public Class Form1 'デリゲートの宣言 Public Delegate Sub RsDelegate() 'データーの受信 Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _ Handles SerialPort1.DataReceived 'データの受信はマルチスレッドで行われる為にデリゲートを使用して 'メインのスレッドでデータ処理の必要があります。 Me.Invoke(New RsDelegate(AddressOf receive_data), New Object() {}) End Sub '受信データー Public Sub receive_data() Dim byteArray(SerialPort1.BytesToRead - 1) As Byte Static GND_Level(9) As Integer Static p As Integer If byteArray.Length <> 4 Then Exit Sub 'ポートのバッファから読み込み SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead) 'ヘッダーチェック If (byteArray(0) <> &H55) Or (byteArray(1) <> &HAA) Then Exit Sub 'ADCの値 Dim ADC As Integer = byteArray(2) * 256 + byteArray(3) Dim speed_max = 500 Dim speed As Integer 'TrackBarが0のとき2.5VGND基準電圧を読み込みます。 If TrackBar1.Value = 0 Then '10回の移動平均をとります。 GND_Level(p) = ADC p += 1 If p > 9 Then p = 0 TrackBar1.Enabled = True '最初の平均が出てからTrackBarをEnableします。 End If End If If TrackBar1.Enabled Then 'スピードメータ− speed = Math.Abs(ADC - GND_Level.Average) Label2.Height = speed / speed_max * Label1.Height Label2.Top = Label1.Top + Label1.Height - Label2.Height End If End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'ポート接続処理 SerialPort1.PortName = ComboBox1.Text SerialPort1.BaudRate = 115200 SerialPort1.Parity = IO.Ports.Parity.None SerialPort1.DataBits = 8 SerialPort1.StopBits = IO.Ports.StopBits.One SerialPort1.ReceivedBytesThreshold = 4 SerialPort1.ReadTimeout = 500 SerialPort1.WriteTimeout = 500 SerialPort1.Open() Timer1.Enabled = True Button1.Enabled = False Button2.Enabled = True ComboBox1.Enabled = False End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'ポート切断処理 Timer1.Enabled = False SerialPort1.Close() ComboBox1.Enabled = True Button1.Enabled = True Button2.Enabled = False TrackBar1.Enabled = False End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick Dim Txdata(3) As Byte 'ヘッダーとして&H55,&HAAを送る Txdata(0) = &H55 Txdata(1) = &HAA 'トラックバーの値を送信します。 Txdata(2) = TrackBar1.Value 'speed data '方向CheckBoxの状態を送ります。 If CheckBox1.Checked Then Txdata(3) = &HFF '逆転のとき&HFFを送る Else Txdata(3) = 0 '正転のとき0を送る End If Try SerialPort1.Write(Txdata, 0, 4) '4BYTEデータ送信 Catch ex As Exception 'エラーのとき Button2_Click(Nothing, Nothing) '切断 MsgBox(ex.Message) End Try End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Dim ports As String() = SerialPort.GetPortNames() If ports.Count = 0 Then MsgBox("シリアルポートが見つかりません。") Button1.Enabled = False Else '使用可能なシリアルポートをコンボボックスに登録 ComboBox1.Items.AddRange(ports) ComboBox1.SelectedIndex = 0 End If Me.Text = "PC Remote" Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D Me.MinimizeBox = False Me.MaximizeBox = False Button1.Text = "接続" Button2.Text = "切断" Button2.Enabled = False '切断ボタンdisable '逆転ボタン用checkboxの設定 CheckBox1.Appearance = Appearance.Button CheckBox1.AutoSize = False CheckBox1.Size = Button1.Size CheckBox1.TextAlign = ContentAlignment.MiddleCenter CheckBox1.Text = "逆転" 'スピードメーターの設定 Label1.Text = "" Label2.Text = "" Label2.BackColor = Color.Pink Label2.Left = Label1.Left Label2.Width = Label1.Width Label2.Height = 0 Label2.Top = Label1.Top + Label1.Height - Label2.Height TrackBar1.Enabled = False TrackBar1.Maximum = 255 TrackBar1.TickFrequency = 10 Timer1.Interval = 200 'タイマーインターバル200ms End Sub End Class
9. 3区間で自動往復運転
●次は3区間を制御して自動で往復運転をします。
・列車が区間1から2、1から0に移動したことを検知して減速して停止後、逆転を繰り返して3区間を自動往復運転します。
・ブレッドボードのスペースを作るためドライブ回路の配置を整理して、とりあえず3区間分のドライブ回路を作ります。
・各区間の電圧を検出するため、CMOSアナログスイッチ(4051)により区間を選択してADC入力に送ります。
【dsPICのコード】
※2013/11/3 区間変更部分変更しています。
// PCリモートによるモーター制御 // 区間移動制御 #include
#include
#include
#include
//16bitのINT型の上位H byte、下位L byte読み込み用共用体 union byte_access { unsigned int INT; // Int Access struct{ // byte Access unsigned char L; unsigned char H; }BYTE; }; //位置情報の構造体 struct PSI_BIT{ unsigned char KUKAN:3; /* 区間 Bit 0-2 */ unsigned char ROSEN:3; /* 路線 Bit 3-5 */ unsigned char SOU:1; /* 相 Bit 6 */ unsigned char DIR:1; /* 方向 Bit 7 */ }; //列車の位置情報の構造体 struct st_position { //現在位置 union { /* Position */ unsigned char BYTE; /* Byte Access */ struct PSI_BIT BIT; /* Bit Access */ } NOW; //前の位置 union { /* Position */ unsigned char BYTE; /* Byte Access */ struct PSI_BIT BIT; /* Bit Access */ } BEFORE; //次の位置 union { /* Position */ unsigned char BYTE; /* Byte Access */ struct PSI_BIT BIT; /* Bit Access */ } NEXT; //次の次の位置 union { /* Position */ unsigned char BYTE; /* Byte Access */ struct PSI_BIT BIT; /* Bit Access */ } ANEXT; unsigned int speed; }; //監視区間設定用の共用体 union scan_port { unsigned char BYTE; /* Byte Access */ struct { unsigned char DUMMY:2; /* Bit 0-1 */ unsigned char KUKAN:3; /* Bit 2-4 監視区間*/ unsigned char DISABLE:1; /* Bit 5 */ unsigned char B6:1; /* Bit 6 */ } BIT; }; // A/D control register unsigned int _ADCON1; unsigned int _ADCON2; unsigned int _ADCON3; unsigned int _ADCHS; unsigned int _ADPCFG; unsigned int _ADCSSL ; // UART control register unsigned int _U1BRG; unsigned int _U1MODE; unsigned int _U1STA; #define Fcy 7370000*4 #define BaudRate 115200 #define RX_BYTE 100 //RS232C受信バイト数 #define TX_BYTE 100 //RS232C送信バイト数 unsigned char RxData[RX_BYTE]; //RS232C受信用メモリ unsigned char TxData[TX_BYTE]; //RS232C送信用メモリ unsigned char EnableBit[] ={1,2,4,8,16,32,64,128}; unsigned int int_counter; //インタラプトカウンタ unsigned int speed; //スピードパルス幅 unsigned int speed_set; //スピード設定 unsigned char direction; //方向 union byte_access speed_ret; //読み取りスピード struct st_position train[1]; //列車位置情報 unsigned char kukan; //区間 union scan_port kanshi; //区間監視用 unsigned char henka; //区間変化 union byte_access adc_ret; //ADC値 void kukan_ON(unsigned char,unsigned char); void kukan_OFF(unsigned char); int main(void) { unsigned char n; //dsPIC基板のブートローダーの制約によりAIVTを使用 INTCON2bits.ALTIVT=1; // A/D変換モジュールオフ確認 _ADON=0; // A/D変換モジュール初期化 (PB9をADCに使用) _ADCHS= ADC_CH0_POS_SAMPLEA_AN9 & ADC_CH0_NEG_SAMPLEA_NVREF; SetChanADC12(_ADCHS); // ADC割り込み禁止 ConfigIntADC12(ADC_INT_DISABLE); _ADCON1=ADC_MODULE_ON & ADC_IDLE_CONTINUE & ADC_FORMAT_INTG & ADC_CLK_AUTO & ADC_AUTO_SAMPLING_OFF & ADC_SAMP_OFF; _ADCON2=ADC_VREF_AVDD_AVSS & ADC_SCAN_OFF & ADC_SAMPLES_PER_INT_1 & ADC_ALT_BUF_OFF & ADC_ALT_INPUT_OFF; _ADCON3=ADC_SAMPLE_TIME_1 & ADC_CONV_CLK_SYSTEM & ADC_CONV_CLK_10Tcy; _ADPCFG=ENABLE_AN9_ANA; _ADCSSL=SCAN_NONE; OpenADC12(_ADCON1, _ADCON2, _ADCON3, _ADPCFG, _ADCSSL); //Timer1設定 OpenTimer1(T1_ON & T1_GATE_OFF & T1_PS_1_1 & T1_SYNC_EXT_OFF & T1_SOURCE_INT, 586); ConfigIntTimer1(T1_INT_PRIOR_5 & T1_INT_ON); // UART1モジュールのオフを確認します CloseUART1(); // UART1割り込み禁止 ConfigIntUART1(UART_RX_INT_DIS & UART_TX_INT_DIS); // UART1の初期化 _U1BRG=((Fcy/BaudRate)/16); // パリティなし、データ8bit、ストップ1bit _U1MODE=UART_EN & UART_IDLE_CON & UART_ALTRX_ALTTX & UART_DIS_WAKE & UART_DIS_LOOPBACK & UART_DIS_ABAUD & UART_NO_PAR_8BIT & UART_1STOPBIT; _U1STA= UART_INT_TX_BUF_EMPTY & UART_TX_PIN_NORMAL & UART_TX_ENABLE & UART_INT_RX_CHAR & UART_ADR_DETECT_DIS & UART_RX_OVERRUN_CLEAR; // Open UART1 OpenUART1(_U1MODE, _U1STA, _U1BRG); _TRISB0=0; //PB0:OUT Portに設定 _TRISB1=0; //PB1:OUT Portに設定 _TRISB9=1; //PB9:ADC入力端子 In Portに設定 TRISF=0; train[0].NOW.BYTE=0; train[0].NOW.BIT.KUKAN=0; train[0].NOW.BIT.DIR=0; train[0].BEFORE.BYTE=0; train[0].BEFORE.BIT.KUKAN=1; train[0].BEFORE.BIT.DIR=0; train[0].NEXT.BYTE=0; train[0].NEXT.BIT.KUKAN=2; train[0].NEXT.BIT.DIR=0; //main loop while(1) { //RS232Cの受信 //ヘッダー確認 while(U1STAbits.URXDA==0); // wait RX data if(ReadUART1() == 0x55){ while(U1STAbits.URXDA==0); // wait RX data if(ReadUART1() == 0xAA){ //0x55,0xAAが続けてきたら正しいデータとして受信 for(n=0;n
0x1ff){ int_counter = 0; } //インタラプトカウンタが0のときspeedパルスON if(int_counter == 0){ speed = train[0].speed; //speedパルス幅のセット //区間メモリに現・前位置の区間を設定し区間ON kukan = EnableBit[train[0].NOW.BIT.KUKAN]; kukan |= EnableBit[train[0].BEFORE.BIT.KUKAN]; kukan_ON(kukan , train[0].NOW.BIT.DIR); } //インタラプトカウンタが0x1のとき区間変化監視 if(int_counter == 0x1){ if(henka==0){ //次位置区間の監視 kanshi.BIT.KUKAN=train[0].NEXT.BIT.KUKAN; LATF &= 0xC0; LATF |= kanshi.BYTE; //ADC読み込み _SAMP=1; while(BusyADC12()); adc_ret.INT = ReadADC12(0); if(train[0].NOW.BIT.DIR==0){ //正方向のとき if(adc_ret.BYTE.H > 0xB){ henka=0x80; //区間変化 } } else{ //逆方向のとき if(adc_ret.BYTE.H < 0x3){ henka=0x80; //区間変化 } } } } //インタラプトカウンタが2のときNEXT区間パルスON //区間変化確認後に次区間もON if(int_counter == 2){ kukan |= EnableBit[train[0].NEXT.BIT.KUKAN]; kukan_ON(kukan , train[0].NOW.BIT.DIR); } //インタラプトごとにspeedをマイナス1して0になったらパルスOFF if(speed == 0){ //speedが0でパルスOFF kukan_OFF(kukan); } else{ speed--; //speedをマイナス1 } //インタラプトカウンタが0x1FFのときADCでモーターの発電電圧読み込み if(int_counter == 0x1FF){ kanshi.BIT.KUKAN=train[0].NOW.BIT.KUKAN; LATF &= 0xC0; LATF |= kanshi.BYTE; _SAMP=1; while(BusyADC12()); speed_ret.INT = ReadADC12(0); } _RF6=0;//割り込み終了監視 _T1IF = 0; //割り込みフラグ解除 } //区間ON用 void kukan_ON(unsigned char kukan,unsigned char d){ // d=0:正回転, 1:負回転 if(d==0) LATB |= kukan; else LATB &= ~kukan; TRISB &= ~kukan; } //区間OFF用 void kukan_OFF(unsigned char kukan){ TRISB |= kukan; }
・列車が区間0と1の間にあるとします。このとき、進行方向側の区間1を現位置、区間0を前位置とします。 進行方向の次の区間2を次位置とします。
・現位置のみ通電すると、接触が悪い場合止まってしまうので、前意位置にも通電します。
・列車が現位置から次位置にかかったときに、次位置の電圧を監視しておいて、区間移動したことを検知します。
・接触が悪く検知が少し遅れたとき列車が止まることがあるので、位置検出後に次位置もパルスONして通電します。
【PC側のソフト】
・8.列車のスピードの検出のソフトにLabelコントロールを追加します。
・また同様に、すべてのコードを削除し、下記のコードを貼り付けます。
※2013/11/3 区間変更部分変更しています。
Imports System.IO.Ports Public Class Form1 Const Tx_Byte = 102 Const Rx_Byte = 102 Class 位置情報 Public 区間 As Integer Public 路線 As Integer Public 方向 As Integer Sub New(ByVal 区 As Integer, ByVal 路 As Integer, ByVal 向 As Integer) 区間 = 区 路線 = 路 方向 = 向 End Sub Public ReadOnly Property Byte情報() As Byte Get Return 区間 + 路線 * 8 + 方向 * &H80 End Get End Property Sub 次区間() If 方向 = 0 Then 区間 = (区間 + 1) And &H7 Else 区間 = (区間 - 1) And &H7 End If End Sub Sub 前区間() If 方向 = 0 Then 区間 = (区間 - 1) And &H7 Else 区間 = (区間 + 1) And &H7 End If End Sub Function copy() Return New 位置情報(区間, 路線, 方向) End Function Sub 逆転() If 方向 = 0 Then 方向 = 1 Else 方向 = 0 End If End Sub End Class Class train Public 現位置 As 位置情報 Public 前位置 As 位置情報 Public 次位置 As 位置情報 Public 区間変化 As Byte Public speed As Single Public 逆転 As Integer Public 設定speed As Integer Sub New(ByVal 現 As 位置情報) 現位置 = 現 前位置 = 現位置.copy 前位置.前区間() 次位置 = 現位置.copy 次位置.次区間() End Sub Sub New(ByVal 現 As 位置情報, ByVal 前 As 位置情報, ByVal 次 As 位置情報) 現位置 = 現 前位置 = 前 次位置 = 次 End Sub Sub 区間変更() 前位置 = 現位置.copy 現位置 = 次位置.copy 次位置.次区間() End Sub Sub 逆転処理() '現位置を前位置に、前位置を現位置に 現位置.逆転() 前位置.逆転() 次位置 = 現位置.copy '前位置に入れるため 現位置 = 前位置.copy 前位置 = 次位置.copy 次位置 = 現位置.copy 次位置.次区間() End Sub End Class Dim 列車(8) As train 'デリゲートの宣言 Public Delegate Sub RsDelegate() 'データーの受信 Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _ Handles SerialPort1.DataReceived 'データの受信はマルチスレッドで行われる為にデリゲートを使用して 'メインのスレッドでデータ処理の必要があります。 Me.Invoke(New RsDelegate(AddressOf receive_data), New Object() {}) End Sub '受信データー Public Sub receive_data() Dim byteArray(SerialPort1.BytesToRead - 1) As Byte Static GND_Level(9) As Integer Static p As Integer Label1.Text = byteArray.Length If byteArray.Length <> Rx_Byte Then Exit Sub 'ポートのバッファから読み込み SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead) 'ヘッダーチェック If (byteArray(0) <> &H55) Or (byteArray(1) <> &HAA) Then Exit Sub 'ADCの値 Dim ADC As Integer = byteArray(2) * 256 + byteArray(3) Dim speed_max = 500 Dim speed As Integer 'TrackBarが0のとき2.5VGND基準電圧を読み込みます。 If TrackBar1.Value = 0 Then '10回の移動平均をとります。 GND_Level(p) = ADC p += 1 If p > 9 Then p = 0 TrackBar1.Enabled = True '最初の平均が出てからTrackBarをEnableします。 End If End If If TrackBar1.Enabled Then 'スピードメータ− speed = Math.Abs(ADC - GND_Level.Average) RectangleShape2.Height = speed / speed_max * RectangleShape1.Height RectangleShape2.Top = RectangleShape1.Top + RectangleShape1.Height - RectangleShape2.Height End If '区間変化の確認 If byteArray(4) = &H80 Then '区間変化 列車(0).区間変更() 列車(0).区間変化 = 1 '列車逆転のため停止させる 列車(0).逆転 = 1 列車(0).設定speed = TrackBar1.Value TrackBar1.Value = 0 Else '区間変化フラグクリア 列車(0).区間変化 = 0 End If 'ラベルに位置表示 Label1.Text = "現:" & 列車(0).現位置.区間 & " 前:" & 列車(0).前位置.区間 & " 次:" & 列車(0).次位置.区間 End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'ポート接続処理 SerialPort1.PortName = ComboBox1.Text 'SerialPort1.PortName = "COM5" '決まっている場合は直接指定 SerialPort1.BaudRate = 115200 SerialPort1.Parity = IO.Ports.Parity.None SerialPort1.DataBits = 8 SerialPort1.StopBits = IO.Ports.StopBits.One SerialPort1.ReceivedBytesThreshold = Rx_Byte SerialPort1.ReadTimeout = 500 SerialPort1.WriteTimeout = 500 SerialPort1.Open() Timer1.Enabled = True Button1.Enabled = False Button2.Enabled = True ComboBox1.Enabled = False End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'ポート切断処理 Timer1.Enabled = False SerialPort1.Close() ComboBox1.Enabled = True Button1.Enabled = True Button2.Enabled = False TrackBar1.Enabled = False End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick '200msタイマー Dim Txdata(Tx_Byte) As Byte Const 加速 As Single = 0.3 '加速割合 'ヘッダーとして&H55,&HAAを送る Txdata(0) = &H55 Txdata(1) = &HAA 'speed 加速・減速 列車(0).speed += (TrackBar1.Value - 列車(0).speed) * 加速 If 列車(0).逆転 = 1 Then If Int(列車(0).speed) = 0 Then '列車が停止したら逆転 列車(0).逆転処理() 列車(0).逆転 = 0 'スピード戻し TrackBar1.Value = 列車(0).設定speed End If End If If 列車(0).現位置.方向 = 1 Then CheckBox1.Checked = True Else CheckBox1.Checked = False End If 'speed値を送信します。 Txdata(2) = Int(列車(0).speed) 'speed data Txdata(3) = 列車(0).現位置.Byte情報 Txdata(4) = 列車(0).前位置.Byte情報 Txdata(5) = 列車(0).次位置.Byte情報 Txdata(6) = 列車(0).区間変化 Try SerialPort1.Write(Txdata, 0, Tx_Byte) 'Tx_BYTEデータ送信 Catch ex As Exception 'エラーのとき Button2_Click(Nothing, Nothing) '切断 MsgBox(ex.Message) End Try End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load '列車情報設定 列車(0) = New train(New 位置情報(1, 0, 0)) Dim ports As String() = SerialPort.GetPortNames() If ports.Count = 0 Then MsgBox("シリアルポートが見つかりません。") : End '使用可能なシリアルポートをコンボボックスに登録 ComboBox1.Items.AddRange(ports) ComboBox1.SelectedIndex = 0 Me.Text = "PC Remote" Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D Me.MinimizeBox = False Me.MaximizeBox = False Button1.Text = "接続" Button2.Text = "切断" Button2.Enabled = False '切断ボタンdisable '逆転ボタン用checkboxの設定 CheckBox1.Appearance = Appearance.Button CheckBox1.AutoSize = False CheckBox1.Size = Button1.Size CheckBox1.TextAlign = ContentAlignment.MiddleCenter CheckBox1.Text = "逆転" CheckBox1.Enabled = False '自動のためDisable 'スピードメーターの設定 RectangleShape2.FillStyle = PowerPacks.FillStyle.Solid RectangleShape2.FillColor = Color.Pink RectangleShape2.Left = RectangleShape1.Left RectangleShape2.Width = RectangleShape1.Width RectangleShape2.Height = 0 RectangleShape2.Top = RectangleShape1.Top + RectangleShape1.Height - RectangleShape2.Height TrackBar1.Enabled = False TrackBar1.Maximum = 255 TrackBar1.TickFrequency = 10 Timer1.Interval = 200 'タイマーインターバル200ms End Sub End Class
・VB2013の場合は、Label3を追加します。
VB2013の場合のコード
Imports System.IO.Ports Public Class Form1 Const Tx_Byte = 102 Const Rx_Byte = 102 Class 位置情報 Public 区間 As Integer Public 路線 As Integer Public 方向 As Integer Sub New(ByVal 区 As Integer, ByVal 路 As Integer, ByVal 向 As Integer) 区間 = 区 路線 = 路 方向 = 向 End Sub Public ReadOnly Property Byte情報() As Byte Get Return 区間 + 路線 * 8 + 方向 * &H80 End Get End Property Sub 次区間() If 方向 = 0 Then 区間 = (区間 + 1) And &H7 Else 区間 = (区間 - 1) And &H7 End If End Sub Sub 前区間() If 方向 = 0 Then 区間 = (区間 - 1) And &H7 Else 区間 = (区間 + 1) And &H7 End If End Sub Function copy() Return New 位置情報(区間, 路線, 方向) End Function Sub 逆転() If 方向 = 0 Then 方向 = 1 Else 方向 = 0 End If End Sub End Class Class train Public 現位置 As 位置情報 Public 前位置 As 位置情報 Public 次位置 As 位置情報 Public 区間変化 As Byte Public speed As Single Public 逆転 As Integer Public 設定speed As Integer Sub New(ByVal 現 As 位置情報) 現位置 = 現 前位置 = 現位置.copy 前位置.前区間() 次位置 = 現位置.copy 次位置.次区間() End Sub Sub New(ByVal 現 As 位置情報, ByVal 前 As 位置情報, ByVal 次 As 位置情報) 現位置 = 現 前位置 = 前 次位置 = 次 End Sub Sub 区間変更() 前位置 = 現位置.copy 現位置 = 次位置.copy 次位置.次区間() End Sub Sub 逆転処理() '現位置を前位置に、前位置を現位置に 現位置.逆転() 前位置.逆転() 次位置 = 現位置.copy '前位置に入れるため 現位置 = 前位置.copy 前位置 = 次位置.copy 次位置 = 現位置.copy 次位置.次区間() End Sub End Class Dim 列車(8) As train 'デリゲートの宣言 Public Delegate Sub RsDelegate() 'データーの受信 Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _ Handles SerialPort1.DataReceived 'データの受信はマルチスレッドで行われる為にデリゲートを使用して 'メインのスレッドでデータ処理の必要があります。 Me.Invoke(New RsDelegate(AddressOf receive_data), New Object() {}) End Sub '受信データー Public Sub receive_data() Dim byteArray(SerialPort1.BytesToRead - 1) As Byte Static GND_Level(9) As Integer Static p As Integer Label3.Text = byteArray.Length If byteArray.Length <> Rx_Byte Then Exit Sub 'ポートのバッファから読み込み SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead) 'ヘッダーチェック If (byteArray(0) <> &H55) Or (byteArray(1) <> &HAA) Then Exit Sub 'ADCの値 Dim ADC As Integer = byteArray(2) * 256 + byteArray(3) Dim speed_max = 500 Dim speed As Integer 'TrackBarが0のとき2.5VGND基準電圧を読み込みます。 If TrackBar1.Value = 0 Then '10回の移動平均をとります。 GND_Level(p) = ADC p += 1 If p > 9 Then p = 0 TrackBar1.Enabled = True '最初の平均が出てからTrackBarをEnableします。 End If End If If TrackBar1.Enabled Then 'スピードメータ− speed = Math.Abs(ADC - GND_Level.Average) Label2.Height = speed / speed_max * Label1.Height Label2.Top = Label1.Top + Label1.Height - Label2.Height End If '区間変化の確認 If byteArray(4) = &H80 Then '区間変化 列車(0).区間変更() 列車(0).区間変化 = 1 '列車逆転のため停止させる 列車(0).逆転 = 1 列車(0).設定speed = TrackBar1.Value TrackBar1.Value = 0 Else '区間変化フラグクリア 列車(0).区間変化 = 0 End If 'ラベルに位置表示 Label3.Text = "現:" & 列車(0).現位置.区間 & " 前:" & 列車(0).前位置.区間 & " 次:" & 列車(0).次位置.区間 End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'ポート接続処理 SerialPort1.PortName = ComboBox1.Text 'SerialPort1.PortName = "COM5" '決まっている場合は直接指定 SerialPort1.BaudRate = 115200 SerialPort1.Parity = IO.Ports.Parity.None SerialPort1.DataBits = 8 SerialPort1.StopBits = IO.Ports.StopBits.One SerialPort1.ReceivedBytesThreshold = Rx_Byte SerialPort1.ReadTimeout = 500 SerialPort1.WriteTimeout = 500 SerialPort1.Open() Timer1.Enabled = True Button1.Enabled = False Button2.Enabled = True ComboBox1.Enabled = False End Sub Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click 'ポート切断処理 Timer1.Enabled = False SerialPort1.Close() ComboBox1.Enabled = True Button1.Enabled = True Button2.Enabled = False TrackBar1.Enabled = False End Sub Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick '200msタイマー Dim Txdata(Tx_Byte) As Byte Const 加速 As Single = 0.3 '加速割合 'ヘッダーとして&H55,&HAAを送る Txdata(0) = &H55 Txdata(1) = &HAA 'speed 加速・減速 列車(0).speed += (TrackBar1.Value - 列車(0).speed) * 加速 If 列車(0).逆転 = 1 Then If Int(列車(0).speed) = 0 Then '列車が停止したら逆転 列車(0).逆転処理() 列車(0).逆転 = 0 'スピード戻し TrackBar1.Value = 列車(0).設定speed End If End If If 列車(0).現位置.方向 = 1 Then CheckBox1.Checked = True Else CheckBox1.Checked = False End If 'speed値を送信します。 Txdata(2) = Int(列車(0).speed) 'speed data Txdata(3) = 列車(0).現位置.Byte情報 Txdata(4) = 列車(0).前位置.Byte情報 Txdata(5) = 列車(0).次位置.Byte情報 Txdata(6) = 列車(0).区間変化 Try SerialPort1.Write(Txdata, 0, Tx_Byte) 'Tx_BYTEデータ送信 Catch ex As Exception 'エラーのとき Button2_Click(Nothing, Nothing) '切断 MsgBox(ex.Message) End Try End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load '列車情報設定 列車(0) = New train(New 位置情報(1, 0, 0)) Dim ports As String() = SerialPort.GetPortNames() If ports.Count = 0 Then MsgBox("シリアルポートが見つかりません。") Button1.Enabled = False Else '使用可能なシリアルポートをコンボボックスに登録 ComboBox1.Items.AddRange(ports) ComboBox1.SelectedIndex = 0 End If Me.Text = "PC Remote" Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D Me.MinimizeBox = False Me.MaximizeBox = False Button1.Text = "接続" Button2.Text = "切断" Button2.Enabled = False '切断ボタンdisable '逆転ボタン用checkboxの設定 CheckBox1.Appearance = Appearance.Button CheckBox1.AutoSize = False CheckBox1.Size = Button1.Size CheckBox1.TextAlign = ContentAlignment.MiddleCenter CheckBox1.Text = "逆転" CheckBox1.Enabled = False '自動のためDisable 'スピードメーターの設定 Label1.Text = "" Label2.Text = "" Label2.BackColor = Color.Pink Label2.Left = Label1.Left Label2.Width = Label1.Width Label2.Height = 0 Label2.Top = Label1.Top + Label1.Height - Label2.Height TrackBar1.Enabled = False TrackBar1.Maximum = 255 TrackBar1.TickFrequency = 10 Timer1.Interval = 200 'タイマーインターバル200ms End Sub End Class
・はじめに区間0か区間1に列車を置きます。
・実行してスライダーを上げると、区間2に進入したところで減速して、完全に停止した後、逆転します。
・次に区間0に進入したところで減速、完全停止後逆転します。以上を繰り返します。
【コードの説明】
・列車の位置情報等を管理するためのクラスを作ります。
Class 位置情報
・PIC側と同じように、区間、路線などを扱います。
Class train
・同じく列車に関する現在位置、前位置、次位置などの位置情報等の情報を扱います。
・区間の変更処理、逆転処理などの関数を実装しています。
宣言 Dim 列車(8) As train → train Classの配列を8つ用意します。(今のところ1つしか使いません)
実装 列車(0) = New train(New 位置情報(1, 0, 0)) → (区間:1、路線:0、方向:正で初期化・実装します。)
タイマー(Timer1)で200ms間隔でdsPICに列車のスピード・位置情報などを送ります。実際は100バイトのブロックを送ります。
dsPIC側でこれを受け取ると、実際のスピード、区間変化情報を送り返してきます。これも100バイトまとめて送ってきます。
【区間変化処理の説明】
・まずdsPIC側の処理ですが、IntCountが0の時駆動パルスをONにしているので、次の割り込みのIntCountが1のとき、次の区間の電圧を監視しておいて、レールを跨いだときに、方向が正の場合は+12V、方向が逆の場合は-12Vを検知します。
・レール電圧は5.1kΩと1kΩの抵抗で分圧した値で約1/6になっていて、レール共通側はdsPICから見て2.5Vなので
+12Vは 12V / 6 + 2.5V = 4.5V
-12Vは -12V / 6 + 2.5V = 0.5V
になります。すなわちdsPICのADC入力は0.5V〜4.5Vの範囲になります。
・そして8入力を選択するCMOSアナログSWの4051の選択bitであるABCに次位置の区間をセットして入力を選択し、ADC入力ポートに入力します。
・プログラムでは正転時は、ADCのHバイトが0xBより大きいとき(約3.5V)、逆転時は0x3より小さいとき(約0.9V)で区間変化を判断しています。
・区間変化を検知したら変化フラグのhenkaを0x80にセットして、このhenkaがRS232CでPCに送信されます。
・PC側ソフトでこれを受信したら、区間変更の処理をして、PC側の区間変化フラグを1にセットします。
・区間変更処理では
前位置←現位置
現位置←次位置
次位置は方向により計算します。
・次のタイマーイベント(200ms後)により新しい位置情報と区間変化フラグをdsPICに送信します。
・dsPIC側でこれらを受信し、henkaフラグを0にクリアし、次の区間変化に備えます。
以上が、区間変化時の処理の流れです。
次へ
PageTopへ戻る