ძალიან ხშირად გვიწევს ერთი მოწყობილობიდან მეორეში ინფორმაციის გაგზავნა ან მიღება, ან თუნდაც მულტი კავშირი მოწყობილობებთან . ხშირად არის ხოლმე სალაპარაკო ენის პრობლემა , თუ რა ენაზე უნდა "ისაუბრონ " ამ მოწყობილობებმა ერთმანეთში, განსაკუთრებით მაშინ , როცა საქმე მიკროკონტროლერებს ეხებათ.
რამდენიმე წლის წინ თავად დავდექი ამ პრობლემის წინაშე, მაშინ მარტივი გამოსავალი ვნახე , სტარტის და სტოპის ბაიტები ჩაუსვი მეხსიერების მასივს და მათ შუა მოვაქციე გასაგზავნი ინფორმაცია, როცა ბევრი პროექტები მქონდა გასაკეთებელი და ხშირად მიწევდა პროტოკოლის გამოყენება მოწყობილობებს შორის, ეს მექანიზმი უკვე აღარ იყო ჩემთვის მისაღები რადგან ყოველი კოდის წერაზე მიწევდა სხვადასხვა კონფიგურაციის შექმნა, რაც მირთულებდა უფრო საქმეს. სწორედ აქ მივიღე გადაწყვეტილება შემექნა მარტივი და უნივერსალური პროტოკოლი , რომელიც დამეხმარებოდა თავიდან ამერიდებინა კონფიგურაციები ყოველი ახალი კოდის წერისას, შევქმენი პროტოკოლი რომელიც წლების განმავლობაში საკმაოდ გამართულად და საიმედოდ მუშაობს.
MST - Microcontroller standard transfer, ანუ მიკროკონტროლერის სტანდარტული ტრანსფერი, ასე ვუწოდე :) ეს არის ე.წ single packet (ასევე მაქვს multi packet , თუმცა მის განხილვას აქ არ ვაპირებ , შემოგთვაზებთ შემდეგ სტატიებში)მექანიზმის პროტოკოლი, რომლის მექანიზმს და სტრუქურას განვიხილავთ.
სურათი 1. MST სტრუქტურა.
SYNC - სინქრონიზაციის ველი, ყოველი პაკეტი იწყება ამ ველით, რათა მივხვდეთ პაკეტის დასაწყისს. ესაა 2 ბაიტის მქონე ველი, სტანდარტულად შედგება 0x7dd7 ამ სიტყვისგან .
ADDR - მოწყობილობის მისამართი, საჭიროა რათა მოვახდინოთ იდენტიფიცირება რომელ მოწყობილობას ვუკავშირდებით(ძირითადათ გამოიყენება მულტიკავშირიან სისტემებში) 2 ბაიტის მქონე ველი , რომელშიც წერია მოწყობილობის მისამართი , თუ ვის ვუგზავნით პაკეტს.
CMD - ბძანების ველი, ამ ველის საშუალებით ვეუბნებით მოწყობილობას თუ რა გვსურს მისგან ან რას ვუგზავნით მას. 2 ბაიტის მქონე ველი.
SUBCMD - ქვებრძანების ველი, ვატყობინებთ მოწყობილობას მთავარი ბძანების ველის მიხედვით რა უნდა შეასრულოს ან რა გამოგვიგზავნოს. მაგალითად მთავარი ბრძანება SET_OUTPUT, რელეების კონფიგურაცია, ქვებრძანებაში მიუთითებთ რომელი რელე ჩართოს ან გამორთოს. 1 ბაიტის მქონე ველი.
LENGTH - აქტიური დატას ზომა ბაიტებში, ამ ველის საშულებით ვიგებთ რა ზომის ინფორმაცია მივიღეთ, მაქსიმალური ველის ზომა 2 ბაიტი ანუ 65535 ბაიტის გაგზავნა -მიღება შეგვიძლია.
PAYLOAD - აქტიური დატა-მონაცემები, ამ ველის დანიშნულებაა გავგზავნოთ ან მივიღოთ ინფორმაცია , მაგალითად მოწყობილობიდან ვკითხულობთ შედარებით დიდი ზომის ინფორმაციას, დაუშვათ ტემპერატურა , ტენიანობა, ძაბვა, დენი, და ა.შ. ამ შემთხვევაში აუცილებელია ეს ინფორმაცია მოვათავსოთ ამ ველში , მივცეთ მისამართი ,ბრძანება თუ რა სახის ინფორმაციაა და გავზავნოთ.
CRC - შემოწმება შეცდომებზე, რათქმაუნდა არის შანსი რომ ინფორმაცია დამახინჯდეს ან ნაწილი მოვიდეს და ა.შ. ამიტომ აუცილებელია შემოწმდეს რომ ინფორმაცია მივიღეთ წარმატებით, ეს არის ყველა ბაიტის XOR ოპერაცია, გარდა სინქრო ბაიტებისა. 1 ბაიტის მქონე ველი.
STOP - პაკეტის დასასრული სიტყვა, ანუ ჰექს მნიშვნელობით 0xA99A. ამით ვხვდებით რომ პაკეტი დასრულდა, განსაკუთრებით მოსახერხებელია როცა ინფორმაციას ვიღებთ წყვეტის ფუნქციაში და ვპარსავთ სათიათაოდ ბაიტებს.
MST პროტოკოლი შეგვიძლია გამოვიყენეთ ისეთ პერიფერიალურ მოწყობილობებთან როგორიცაა მაგალითად SPI, I2C, UART(RS485, RS422), WIEGAND და ა.შ. ასევე კარგად მუშაობს იმ შემთხვევაში თუ მაგალითად სერვერზე ვგზავნით ინფორმაციას ან თუნდაც მობილურ აპლიკაციაში.
სტანდარტული ბრძანებების და შეცდომის კოდების სია:
სენსორები:
SET_OUTPUT 0XA
SET_TEMPERATURE 0XB
SET_HUMIDITY 0XC
SET_SERVO 0XD
SET_STEPPER 0XE
SET_DC_MOTOR 0XF
SET_CO2 0X10
SET_ACCELEROMETER 0X11
SET_COMPASS 0X12
SET_VOLTAGE 0X13
SET_CURRENT 0X14
SET_FREQUENCY 0X15
SET_DURATION 0X16
SET_PWM 0X17
SET_TIME 0X18
SET_DATE 0X19
GET_INPUT 0X64
GET_TEMPERATURE 0X65
GET_HUMIDITY 0X66
GET_SERVO 0X67
GET_STEPPER 0X68
GET_DC_MOTOR 0X69
GET_CO2 0X6A
GET_ACCELEROMETER 0X6B
GET_COMPASS 0X6C
GET_VOLTAGE 0X6D
GET_CURRENT 0X6E
GET_FREQUENCY 0X6F
GET_DURATION 0X70
GET_PWM 0X71
GET_TIME 0X72
GET_DATE 0X73
GET_ADC 0X74
GET_DAC 0X75
შეცდომები:
ERROR_WRITE 0XC8
ERROR_READ 0XC9
ERROR_SYSTEM 0XCA
ERROR_TEMP_SENSOR 0XCB
ERROR_ACCELEROMETER 0XCC
ERROR_COMPASS 0XCD
ERROR_ADC 0XCE
ERROR_DAC 0XCF
ERROR_RTC 0XD0
ERROR_OW_CAMERA 0XD1
ERROR_EEP_MEMORY 0XD2
ERROR_FLASH_MEMORY 0XD3
ERROR_CARD_READER 0XD4
ERROR_GSM_MODEM 0XD5
ERROR_GPS_MODEM 0XD6
ERROR_GPS_WIFI 0XD7
ERROR_BLUETOOTH 0XD8
ქვებრძანებები:
CHANNEL1-CHANNEL32 - 0X1 - 0X20
DEVICE1 - DEVICE8 - 0X20 - 0X28
კოდი:
UINT8 MST_init(mst_info *mst, UINT16 tx_length, UINT16 rx_length, UINT16 queue_length);
ფუნქცია რომელიც ქმნის ბუფერებს ინფორმაციის გაგზავნა მიღებისთის , ასევე , რიგის ბუფერს. არგუმენტებად ვუთითებთ სასურველ ზომას. თუ დინამიურად ვერ გამოიყო მეხსიერების ადგილი აბრუნებს შეცდომას.
INT16 MST_create_packet(mst_info *mst, UINT16 addr, UINT16 cmd, UINT8 subcmd, UINT8 *pdata, UINT16 length);
ქმნის პაკეტს , არგუმენტებად გადავცემთ , მისამართს, ბრძანებას, ქვებრძანებას , დატა ბუფერს სადაც გვაქვს აქტიური ინფორმაცია(შესაძლოა არც გვქონდეს) და ამ ბუფერის ზომას. აბრუნებს შეცდომას თუ ბუფერის ზომა აღემატება გამოყოფილი მეხსიერების ზომას.
UINT8 MST_get_packet(mst_info *mst);
პაკეტის სტეიტ მანქანა, იღებს პაკეტს და აბრუნებს მდგომარეობას, მიიღო პაკეტი, CRC შეცდომა მოხდა, თუ ტაიმაუტში გავიდა .
UINT16 MST_get_addr(mst_info *mst);
მიებული პაკეტიდან აბრუნებს მისამართს.
UINT16 MST_get_cmd(mst_info *mst);
მიღებული პაკეტიდან აბრუნებს ბრძანებას.
UINT8 MST_get_subcmd(mst_info *mst);
მიღებული პაკეტიდან აბრუნებს ქვებრძანებას.
UINT16 MST_get_length(mst_info *mst);
მიებული პაკეტიდან აბრუნებს აქტიური დატას ზომას.
void MST_get_payload(mst_info *mst, UINT8 *payload);
მიღებული პაკეტიდან აკოპირებს აქტიურ დატას ბუფერში.
UINT8 MST_queue_push(mst_info *mst, UINT8 b);
ეს ფუნქცია ისმევა პერიფერიალური მოწყობილობის წვეტის ფუნქციაში, და რიგის ბუფერში ყრის მიებულ ბაიტებს.
კოდი დაწერილი მაქვს სტმ32 ის მიკროკონტროლერებისთვის C ენაზე, შესაძლებელია არდუინოს გარემოსთვის მარტივად გადაკეთება.
// File Name : MST_protocol.h
// Author : Giorgi Kurtanidze
// Date First Issued : 07/05/2013 : Version V01.01
// Description : Microcontroller standart transfer (MST) protocol
#ifndef _MST_PROTOCOL_H
#define _MST_PROTOCOL_H
#define MST_HEADER 15
#define MST_SYNC 0x7dd7
#define MST_STOP 0xA99A
#define MST_RX_DELAY 500
typedef struct
{
UINT8 *tx_buff;
UINT8 *rx_buff;
queue_info queue_buff;
UINT16 tx_buff_length;
UINT16 rx_buff_length;
UINT16 queue_buff_length;
UINT8 rx_packet_index;
}mst_info;
UINT8 MST_init(mst_info *mst, UINT16 tx_length, UINT16 rx_length, UINT16 queue_length);
UINT16 MST_create_packet(mst_info *mst, UINT16 addr, UINT16 cmd, UINT8 subcmd, UINT8 *pdata, UINT16 length);
UINT8 MST_get_packet(mst_info *mst);
UINT16 MST_get_addr(mst_info *mst);
UINT16 MST_get_cmd(mst_info *mst);
UINT8 MST_get_subcmd(mst_info *mst);
UINT16 MST_get_length(mst_info *mst);
void MST_get_payload(mst_info *mst, UINT8 *payload);
UINT8 MST_queue_push(mst_info *mst, UINT8 b);
#endif
enum
{
MST_RX_GET_SYNC1,
MST_RX_GET_SYNC2,
MST_RX_GET_PACKET
};
thread_timer_info mst_rx_timeout;
/*
2 byte 2 byte 2 byte 1 byte 2 byte n byte 1 byte 2 byte
_____________________________________________________________________
| | | | | | | | |
| SYNC | ADDR | CMD | SUBCMD | LENGTH | PAYLOAD | CRC | STOP |
|_______|_______|_______|________|________|_________|_______|_________|
*/
UINT8 MST_init(mst_info *mst, UINT16 tx_length, UINT16 rx_length, UINT16 queue_length)
{
UINT8 ret;
if(tx_length)
{
mst->tx_buff = (UINT8*) malloc(tx_length * sizeof(UINT8));
if(!mst->tx_buff)
{
trace_gerror("MST mem");
return 0;
}
mst->tx_buff_length = tx_length;
}
if(rx_length)
{
mst->rx_buff = (UINT8*) malloc(rx_length * sizeof(UINT8));
if(!mst->rx_buff)
{
trace_gerror("MST mem");
return 0;
}
mst->rx_buff_length = rx_length;
mst->rx_packet_index = MST_RX_GET_SYNC1;
}
if(queue_length)
{
ret = q_init(&mst->queue_buff, queue_length);
if(ret)
{
trace_gerror("MST mem");
return 0;
}
mst->queue_buff_length = queue_length;
}
return RET_OK;
}
UINT16 MST_create_packet(mst_info *mst, UINT16 addr, UINT16 cmd, UINT8 subcmd, UINT8 *pdata, UINT16 length)
{
UINT16 i;
UINT8 crc = 0;
if(length + MST_HEADER > mst->tx_buff_length)
{
trace_gerror("MST length");
return 0;
}
mst->tx_buff[0] = (UINT8)(MST_SYNC >> 8) & 0xFF;
mst->tx_buff[1] = (UINT8)MST_SYNC & 0xFF;
crc ^= mst->tx_buff[2] = (UINT8)(addr >> 8) & 0xFF;
crc ^= mst->tx_buff[3] = (UINT8)addr & 0xFF;
crc ^= mst->tx_buff[4] = (UINT8)(cmd >> 8) & 0xFF;
crc ^= mst->tx_buff[5] = (UINT8)cmd & 0xFF;
crc ^= mst->tx_buff[6] = (UINT8)subcmd & 0xFF;
crc ^= mst->tx_buff[7] = (UINT8)(length >> 8) & 0xFF;
crc ^= mst->tx_buff[8] = (UINT8)length & 0xFF;
for(i = 0; i < length; i++)
{
crc ^= mst->tx_buff[9+i] = pdata[i];
}
mst->tx_buff[9+i] = crc;
mst->tx_buff[10+i] = (UINT8)(MST_STOP >> 8) & 0xFF;
mst->tx_buff[11+i] = (UINT8) MST_STOP & 0xFF;
return (length + MST_HEADER);
}
UINT8 MST_get_packet(mst_info *mst)
{
static UINT8 b;
static UINT8 crc;
static UINT16 n;
static UINT8 stop_state;
UINT8 sync, stop_msb, stop_lsb;
if(q_get_length(&mst->queue_buff))
{
while(q_get_length(&mst->queue_buff))
{
b = q_pop(&mst->queue_buff);
switch(mst->rx_packet_index)
{
case MST_RX_GET_SYNC1:
sync = (MST_SYNC >> 8) & 0XFF; // sync high byte
if(sync == b) mst->rx_packet_index = MST_RX_GET_SYNC2;
break;
case MST_RX_GET_SYNC2:
mst->rx_packet_index = MST_RX_GET_SYNC1;
sync = MST_SYNC & 0XFF; // sync low byte
if(sync == b)
{
n = 0;
crc = 0;
stop_state = 0;
tol_mem_fill(mst->rx_buff, mst->rx_buff_length, 0);
mst->rx_packet_index = MST_RX_GET_PACKET;
thread_tmr_start(&mst_rx_timeout);
}
break;
case MST_RX_GET_PACKET:
stop_msb = (MST_STOP >> 8) & 0xFF;
stop_lsb = MST_STOP & 0xFF;
if(!stop_state && stop_msb == b)
{
stop_state = 1;
}
else if(stop_state)
{
mst->rx_packet_index = MST_RX_GET_SYNC1;
if(stop_lsb == b)
{
if(crc) return STATE_CRC_ERROR;
return STATE_DONE;
}
return STATE_ERROR;
}
if(!stop_state && n < mst->rx_buff_length)
crc ^= mst->rx_buff[n++] = b;
if(thread_tmr_check(&mst_rx_timeout, MST_RX_DELAY) == THREAD_DONE)
{
mst->rx_packet_index = MST_RX_GET_SYNC1;
return STATE_TIMEOUT;
}
return STATE_BUSY;
}
}
}
return STATE_IDLE;
}
UINT16 MST_get_addr(mst_info *mst)
{
UINT16 addr;
addr = (mst->rx_buff[0] << 8) | mst->rx_buff[1];
return addr;
}
UINT16 MST_get_cmd(mst_info *mst)
{
UINT16 cmd;
cmd = (mst->rx_buff[2] << 8) | mst->rx_buff[3];
return cmd;
}
UINT8 MST_get_subcmd(mst_info *mst)
{
return mst->rx_buff[4];
}
UINT16 MST_get_length(mst_info *mst)
{
UINT16 length;
length = (mst->rx_buff[5] << 8) | mst->rx_buff[6];
return length;
}
void MST_get_payload(mst_info *mst, UINT8 *payload)
{
UINT16 i;
UINT16 l = MST_get_length(mst);
for(i = 0; i < l; i++) payload[i] = mst->rx_buff[7+i];
}
UINT8 MST_queue_push(mst_info *mst, UINT8 b)
{
UINT8 ret;
ret = q_push(&mst->queue_buff, b);
if(ret) return ret;
return RET_OK;
}
Comments
Post a Comment