51単片機modbusRTUスレーブ設計に基づく
modbusプロトコルを理解した後、このプロトコルに基づく設計になります.次に、航太電子HTM 52単片機に基づくスレーブ設計について説明します.
設計思想は以下の通りである.
modbusプロトコルは主従的に通信する、すなわち上位機が命令を送信し、下位機が応答するメカニズムであり、通信を開始するのはずっと上位機であり、下位機は応答すればよい.
modbusプロトコルはPLCアプリケーションに対して設計されています.ここでは簡単にPLC環境をシミュレートすることができます.単片機の中に共有領域を設計することができます.この領域は上位機と下位機が共有しています.いずれもこの領域の値を読み取るか書き込むことができます.すべてのmodbusプロトコルはこの高速領域に対する操作で、下位機もこの領域の値に応じて操作します.
この共有領域は構造体で表され、ここでは2つの変数しか使用しません.
メイン関数では、この領域の値をクエリーするだけで、対応するアクションを実行できます.
次にmodbusプロトコルが具体的にどのように実現されているかを見ると、主関数ではこのプロトコルに参加していないことがわかります.つまり、modbusプロトコルに相当する実装は別のスレッドであり、主関数は実装の詳細に関心を持つ必要はありません.このようなメリットは、主関数が自分の実装タスクに近いことです.タスクのパラメータがどこから来たのかを考慮する必要はありません.
51単片機と上位機との通信はシリアルポート方式を採用し、シリアルポート中断はデータの受信と送信を担当し、ここでは現在のmodbusの状態を監視し、このフレームデータが完了したかどうかを判断し、1フレームデータの受信が完了したと判断した場合、そのフレームデータを解析し、対応する命令を実行するタイマも使用した.
注意rec_time_outこの変数、この変数はタイマ割り込みの中で絶えず自加して、しかしシリアルポート割り込みの中でクリアして、このようにする意味は1フレームのデータが受信して完成するかどうかを判断して、もしrec_time_outこの変数値はある値より大きく,一定期間データ受信がないことを示し,データ受信受信と考えられるが,当然上位機側は1フレームデータが連続的に送信されることを満たさなければならない
シリアル割り込みプログラムは以下の通りです.ここではシリアル割り込み送信データフレームを使用しています.具体的な解析は私の別のブログを参照してください.http://blog.csdn.net/liucheng5037/article/details/48831993:
タイマ実装関数、タイムアウト検出方法に注意:
1フレームのデータ受信に成功すると、実行方法は関数function_MODBUSでは、以下のように命令解析と発動はmodbusプロトコルに厳格に従って来ていますが、ここではプロトコルの一般的ないくつかの命令を使っているだけで、自由に拡張することができます.
51単片機modbus下位機に基づいて設計はここで終わります.この方法は比較的柔軟で、プロトコルの実現を単独で1階に置いて、主関数とのインタラクションを避けます.この工事の位置は私のgitアドレスに注目してください.
https://github.com/zhui-ying/PC_MCU51Project/
中には複数の工事がありますが、設計方法は一致しています.
設計思想は以下の通りである.
modbusプロトコルは主従的に通信する、すなわち上位機が命令を送信し、下位機が応答するメカニズムであり、通信を開始するのはずっと上位機であり、下位機は応答すればよい.
modbusプロトコルはPLCアプリケーションに対して設計されています.ここでは簡単にPLC環境をシミュレートすることができます.単片機の中に共有領域を設計することができます.この領域は上位機と下位機が共有しています.いずれもこの領域の値を読み取るか書き込むことができます.すべてのmodbusプロトコルはこの高速領域に対する操作で、下位機もこの領域の値に応じて操作します.
この共有領域は構造体で表され、ここでは2つの変数しか使用しません.
/*modbus 16 , 0000H, 16 int , */
struct MODBUS_ADD{
int LED_value;// :0000H LED , 8 LED1--LED8
int LED_ctrl;// :0001H
};
struct MODBUS_ADD modbus_Addt;// modbus
struct MODBUS_ADD *modbusAdd;// ,
メイン関数では、この領域の値をクエリーするだけで、対応するアクションを実行できます.
void main()
{
SystemInit();
init_MODBUS();
modbus_Addt.LED_ctrl = COMM_PC;
while(1)
{
//
/*start*/
if(modbus_Addt.LED_ctrl != COMM_PC)
{
modbus_Addt.LED_value = LED_PORT;
}
/*end*/
//
/*start*/
switch(modbus_Addt.LED_ctrl)
{
case COMM_PC:
LED_PORT = ~(uchar)(modbus_Addt.LED_value & 0x00ff);
break;
case COMM_FLOW:
LedFlow();
break;
default:
LED_PORT = ~(uchar)(modbus_Addt.LED_value & 0x00ff);
break;
}
/*end*/
}
}
次にmodbusプロトコルが具体的にどのように実現されているかを見ると、主関数ではこのプロトコルに参加していないことがわかります.つまり、modbusプロトコルに相当する実装は別のスレッドであり、主関数は実装の詳細に関心を持つ必要はありません.このようなメリットは、主関数が自分の実装タスクに近いことです.タスクのパラメータがどこから来たのかを考慮する必要はありません.
51単片機と上位機との通信はシリアルポート方式を採用し、シリアルポート中断はデータの受信と送信を担当し、ここでは現在のmodbusの状態を監視し、このフレームデータが完了したかどうかを判断し、1フレームデータの受信が完了したと判断した場合、そのフレームデータを解析し、対応する命令を実行するタイマも使用した.
注意rec_time_outこの変数、この変数はタイマ割り込みの中で絶えず自加して、しかしシリアルポート割り込みの中でクリアして、このようにする意味は1フレームのデータが受信して完成するかどうかを判断して、もしrec_time_outこの変数値はある値より大きく,一定期間データ受信がないことを示し,データ受信受信と考えられるが,当然上位機側は1フレームデータが連続的に送信されることを満たさなければならない
シリアル割り込みプログラムは以下の通りです.ここではシリアル割り込み送信データフレームを使用しています.具体的な解析は私の別のブログを参照してください.http://blog.csdn.net/liucheng5037/article/details/48831993:
//
void SerISR() interrupt 4 using 2
{
if(RI == 1)
{
unsigned char data_value;
RI=0;
if(send_buf.busy_falg == 1) return;//
data_value = SBUF;
rec_time_out = 0;// ,
switch(rec_stat)
{
case PACK_START:
rec_num = 0;
if(data_value == PACK_START)// ,
{
modbus_recv_buf[rec_num++] = data_value;
rec_stat = PACK_REC_ING;
}
else
{
rec_stat = PACK_ADDR_ERR;
}
break;
case PACK_REC_ING: //
modbus_recv_buf[rec_num++] = data_value;
break;
case PACK_ADDR_ERR: //
break;
default : break;
}
}
if(TI == 1) // ,
{
TI = 0;
send_buf.index++;
if(send_buf.index >= send_buf.length)
{
send_buf.busy_falg = 0;//
return;
}
SBUF = send_buf.buf[send_buf.index];//
}
}
タイマ実装関数、タイムアウト検出方法に注意:
/* 1ms*/
void Time0ISR() interrupt 1 using 1
{
TL0 = T1MS; //reload timer0 low byte
TH0 = T1MS >> 8; //reload timer0 high byte
if(PACK_REC_OK == time_out_check_MODBUS())
{
// , modbus ,
function_MODBUS(modbus_recv_buf);
}
}
/* , 1ms , */
int time_out_check_MODBUS(void)
{
rec_time_out++;
if(rec_time_out == 9) // 5ms,
{
rec_stat = PACK_START;
rec_num = 0;
}
else if((rec_time_out == 4) && (rec_num > 4)) // 4ms
{
rec_stat = PACK_REC_OK;
// modbus_rtu->rec_num = 0;
}
return rec_stat;
}
1フレームのデータ受信に成功すると、実行方法は関数function_MODBUSでは、以下のように命令解析と発動はmodbusプロトコルに厳格に従って来ていますが、ここではプロトコルの一般的ないくつかの命令を使っているだけで、自由に拡張することができます.
void function_MODBUS(unsigned char *rec_buff)
{
switch(rec_buff[1]) //
{
case 1: // 01 : ( ) (ON/OFF)
//read_coil();
break;
case 2: //02 : (ON/OFF)
//read_input_bit();
break;
case 3: //03 :
read_reg(rec_buff);
break;
case 4: //04 :
read_reg(rec_buff);
break;
case 5: //05 : ( ) ( ) ( ) (ON/OFF)
//force_coil_bit();
break;
case 6: //06 : ( )
force_reg(rec_buff);
break;
case 15:
//force_coil_mul();
break;
case 16: //16 : ( )
force_reg(rec_buff);
break;
default:
//modbus_send_buff[1] = rec_buff[1] | 0X80;
//modbus_send_buff[2] = ERR_FUN_CODE; //
//send_num = 5;
break;
}
rec_stat = PACK_START;//
rec_num = 0;
}
/*
function: modbus 03,04
input:rec_buf send_data
*/
void read_reg(unsigned char * rec_buff)
{
unsigned char begin_add = 0;
unsigned char data_num = 0;
unsigned char *piont;
unsigned int send_CRC;
unsigned int send_num;
int i;
begin_add = rec_buff[3]*2;// 1
data_num = rec_buff[5]*2;//
send_num = 5 + data_num; // 5 + addr1 + fun1 + num1 +【data】+ crc2
rec_buff[2] = data_num;//
piont = (unsigned char *)modbusAdd; // ,
for(i=0;i {
rec_buff[3+i] = piont[begin_add +i];
}
send_CRC = comp_crc16(rec_buff, send_num-2);
rec_buff[send_num-2] = send_CRC >> 8;
rec_buff[send_num -1] = send_CRC;
send_count = send_num;
PutNChar(rec_buff , send_count);
}
/*
function: modbus 06 16,
input:rec_buf send_data
*/
void force_reg(unsigned char * rec_buf)
{
unsigned char fun_code,begin_add,data_num;// , ,
unsigned int send_num;//
unsigned char *piont;
unsigned int send_CRC;
int i;
// send_data[0] = rec_buf[0]; //
fun_code = rec_buf[1]; //
// send_data[1] = fun_code;
// send_data[2] = rec_buf[2];//
// send_data[3] = rec_buf[3];
begin_add = rec_buf[3]*2;
piont = (unsigned char *)modbusAdd; // ,
if(fun_code == 6)// ,
{
piont[begin_add] = rec_buf[4];//
piont[begin_add+1] = rec_buf[5];//
send_num = 8;//
}
else if(fun_code == 16)//
{
data_num = rec_buf[5]*2;
send_num = 8;
for(i=0;i {
piont[begin_add+i] = rec_buf[7+i];
}
}
send_CRC = comp_crc16(rec_buf, send_num-2);//CRC
rec_buf[send_num-2] = send_CRC >> 8;
rec_buf[send_num -1] = send_CRC;
send_count = send_num;
PutNChar(rec_buf , send_count);
}
51単片機modbus下位機に基づいて設計はここで終わります.この方法は比較的柔軟で、プロトコルの実現を単独で1階に置いて、主関数とのインタラクションを避けます.この工事の位置は私のgitアドレスに注目してください.
https://github.com/zhui-ying/PC_MCU51Project/
中には複数の工事がありますが、設計方法は一致しています.