Skip to main content

MST პროტოკოლი



ძალიან ხშირად გვიწევს ერთი მოწყობილობიდან მეორეში ინფორმაციის გაგზავნა ან მიღება, ან თუნდაც მულტი კავშირი მოწყობილობებთან . ხშირად არის ხოლმე სალაპარაკო ენის პრობლემა , თუ რა ენაზე უნდა "ისაუბრონ " ამ მოწყობილობებმა ერთმანეთში, განსაკუთრებით მაშინ , როცა საქმე მიკროკონტროლერებს ეხებათ.
რამდენიმე წლის წინ თავად დავდექი ამ პრობლემის წინაშე, მაშინ მარტივი  გამოსავალი ვნახე , სტარტის და სტოპის ბაიტები ჩაუსვი მეხსიერების მასივს და მათ შუა მოვაქციე გასაგზავნი ინფორმაცია, როცა ბევრი პროექტები მქონდა გასაკეთებელი და ხშირად მიწევდა პროტოკოლის გამოყენება მოწყობილობებს შორის, ეს მექანიზმი უკვე აღარ იყო ჩემთვის მისაღები რადგან ყოველი კოდის წერაზე მიწევდა სხვადასხვა კონფიგურაციის შექმნა, რაც მირთულებდა უფრო საქმეს. სწორედ აქ მივიღე გადაწყვეტილება შემექნა მარტივი და უნივერსალური პროტოკოლი , რომელიც დამეხმარებოდა თავიდან ამერიდებინა კონფიგურაციები ყოველი ახალი კოდის წერისას, შევქმენი პროტოკოლი რომელიც  წლების განმავლობაში საკმაოდ გამართულად და საიმედოდ მუშაობს.
 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

Popular posts from this blog

USB ინტერფეისი და პროტოკოლი

         რა არის USB?    ინგლისურიდან თუ ვთარგმნით (Universal Serial bus)  ანუ, უნივერსალური მიმდევრობითი სალტე. დღეისობით USB ფართოდ გამოიყენება კომპიუტერულ სისტემებში, როგორც ინფორმაციის გაცვლის მიმდევრობითი ინტერფეისი. სამომხმარებლო თვალსაზრისით ფაქტობრივად , კაბელით ინფორმაციის გაცვლის სტანდარტად იქცა. სურათი 1.  USB ფიზიკური შეერთება. როგორც სურათიდან ხედავთ, USB სტანდარტში, ინფორმაციის გაცვლის ხაზების(კონტაქტები) გარდა , გვაქვს კვების გამტარები, ეს კი საშუალებას გვაძლევს მაგ. ლეპტოპიდან, მოწყობილობა ვკვებოთ USB პორტიდან, USB კვების ძაბვა სტანდარტულად შეადგენს  5 ვოლტს, მაქს. დენი დაბალი მოხმარების მოწყობილობებისთვის შეადგენს 100მა , მაღალი მოხმარების მოწყობილობებისთის 500მა.(უფრო მაღალი მოხმარების მოწყობილობებს უნდა ქონდეთ დამოუკიდებელი კვების ბლოკი) USB  კავშირის დროს ინიცაიტორი არის ყოველთვის ჰოსტი(maseter/slave პრინციპი), მაგალითად როდესაც ჩვენ კომპიუტერთან ვაერთებთ USB  მოწყობილობას, იმის და მიხედვით თუ რა სიჩქარეზე მუშაობს(ამქაჩველი რეზისტორი, იხილეთ ქვემოთ)მო

სიმ800, სიმ900 პირველი ნაბიჯები...

გამარჯობათ მეგობრებო, განვიხილავთ ზემოთ ნახსენები მოდემების ფიზიკურ მიერთებას მიკროკონტროლერთან და  სამუშაო კოდს. ფიზიკური მიერთება: სადამ მიერთების განხილვას დავიწყებდეთ, ვიტყვი რომ, მოდემის სამუშაო ძაბვა არის 3.4V დან 4.4V მდე, ოპტიმალური ძაბვა 4 ვოლტი. როდესაც მოდემი გადის რეგისტრაციას(კვების მიერთების შემდეგ) მოხმარებული პიკური დენი აღწევს 2 ამპერამდე, ასევე როდესაც ირთვება რადიო გადამცემის ნაწილი(ინფორმაციის გაგზავნისას) ამიტომ , კვების წყარო უნდა უზრუნველყოფდეს ამ სიმძლავრეს , თუ არ გვაქვს 2 ამპერიანი წყარო , შეგვიძლია მივაერთოდ 1 ამპერიანი, ასეთ შემთხვევაში აუცილებელია მოდემის კვების პარალელურად ჩავსვათ 1500-2200 მიკროფარადიანი კონდენსატორი რაც შეიძლება მოდემთან ახლოს. ელექტროლიტური კონდენსატორის დაყენება აუცილებელია ასევე იმ შემთხვევაშიც კი, როდესაც ვიყენებთ 2 ამპერიან კვების წყაროს, თუმცა ასეთ შემთხვევაში 220-470 მიკროფარადიანიც საკმარისია(თუ მოდულს ვიყენებთ აუცილებლად ეყენება ასეთი კონდენსატორი) . იმ შემთხვევაში თუ დაფას ჩვენ ვხაზავთ , აუცილებლად უნდა გავითვალისწინოთ კ