가장 많이 본 글

이 블로그 검색

2018년 6월 8일 금요일

Processing(Interpreting) commands


Processing(Interpreting) commands

After getting hashcode, then processing commands is very simple.
Firstly, check the rx buffer integrity if some errors exist. Then use switch statement to identify command and then clear buffer for the next. Below are the example. Call CommandProcess() from main().

Prepare flags and enumerations for the Serial port.

/* Defines status struct */
struct rsMap{
    // RS232 flags
    unsigned tx_in_progress : 1; // holding high until tx ends
    unsigned rx_in_progress : 1; // holding high until rx terminates by timeout
    unsigned command_received : 1; // set if rx buf has a command
    unsigned command_reserved : 1; // set if a reporting process is progressing
    unsigned comm_error : 1; // set if there's an error in command
    unsigned emergency : 1; // set if emergency rxed
    unsigned remote_on : 1; // set if remote command rxed
    unsigned comm_test : 1; // set if test needed

    unsigned no_response_req : 1; // set if either unknown command or other's data
    unsigned tx_disable : 1; // set if the last data has been written to tx buf
    // in uart, then tx_enable will be disabled by the
    // timer after about 3 mS
unsigned rxResponse : 3; /* expansion of no_response_req */
};

struct rsMap rs;
enum{
    comm1, comm2, comm3, comm4
} cmdSource;

enum{
    noResp, sendACK, sendNAK, sendStatus

} rxResponse;

/* Check received command format */

uint8 checkCmdFormat(void) {

    uint16 i, argCnt;

    if(!rs.comm_error) // Check Communication format error
    {
        /* check command error. identify no response. command error included. */

        rs.no_response_req = 0;

        /* when exceed max_cmd_len. */
        /* rx_count has char count received. */
        if(rx_count > max_cmd_len) rs.no_response_req = 1; // no response for length mismatch.

        // comment the following 2 lines for none cr/lf termination command
        if((rx_buf[rx_count - 1] != cr) && (rx_buf[rx_count - 1] != lf)) rs.no_response_req = 1;
        if((rx_buf[rx_count - 2] != cr) && (rx_buf[rx_count - 2] != lf)) rs.no_response_req = 1;

        // check if there's a non ASCII code
        for(i = 0; i <= rx_count - 3; i++) {
            if(rx_buf[i] < 0x20 || rx_buf[i] > 0x7f) {
                rs.no_response_req = 1;
                break;
            }
        }
        // command code mismatch causes no response.

        /* Extract each command string */
        if(!rs.comm_error && !rs.no_response_req) argCnt = cmdExtractor();

    }
    return argCnt;
}

void convToUpperCaseString(char *sPtr) {
    while(*sPtr != '\0') {
        if(islower(*sPtr)) *sPtr = toupper(*sPtr);
        sPtr++;
    }
}

void CommandProcess(void) {

    uint16 i, cmdOffset, argCnt;
    uint16 l;
    int iTemp;
    float f1, f2;

    /* Check command format and argument list before processing */
    argCnt = checkCmdFormat(); 

    if(!rs.comm_error && !rs.no_response_req) // do normal process
    {

        cmdOffset = cmdOffset; // set command string offset
        rs.rxResponse = noResp; /* Disable response */

        uint32 cmdCode[15];

            
        /* Command processing  ----------------------------------*/

        /* Get all hashcode from the received command -------*/
        if(argCnt > 10) argCnt = 10;
        /* Ptn download has big argument count */
        for(i = 0; i <= argCnt - 1; i++) {
            /* Convert all strings to Uppercase */
            convToUpperCaseString(cmdString[cmdOffset + i]);
            /* Get hashCode */
            cmdCode[i] = getHash(cmdString[cmdOffset + i]);
        }

        /* Jump to appropreate routine -----------------------*/
        switch(cmdCode[0]) {
            case hsOPR: /* Operation commands ----------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsAUTO: /* Auto commands --------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsMAN: /* Manual commands -------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsCCL: /* Cycle commmands -------------------*/
            {
                /* Do appropriate works */
                break;
            }
            case hsSD: /* SD memory commands ----------------*/
            {
                /* Do appropriate works */
                break;
            }
            

            default:
            {
                /* Unknown commmand error */

                           /* Do appropriate works */
                break;
            }
        }
    }

    /* Send Response  -------------------------------------------*/
    if(rs3.rxResponse) {
        switch(rs3.rxResponse) {
            case noResp:
            {
                /* Do nothing */
                break;
            }
            case sendACK:
            {
                /* Do appropriate works */

                break;
            }
            case sendNAK:
            {
                
                /* Do appropriate works */

                break;
            }
            case sendStatus:
            {
                /* Do appropriate works */
                break;
            }

            /* Insert more cases here */

            default: break;
        }
    }

    /* reset buffer consts after use */

    rx_count = 0;
    rs.no_response_req = 0;
    rs.command_received = 0;
    rs.comm_error = 0;
}



2018년 6월 5일 화요일

Making hashcode to interpret commands

On Using hashcode

    Using hashcode makes simple decode command but needs some preparation. A hashcode is a code that a series of characters produce always the same code. So a number of commands have unique the number of hashcodes. If we don't use those codes, we have to use a bunch of string compare and if...else if...else if blocks of code. The if...else if...else if blocks are not all that bad, but they have less readability and you might not see the program again if there are 20 or more commands.

Write the following 2 subroutines to get hash table.

#define hashSeed 0x65559
uint32 getHash(char *Command) {
    uint32 hash = 0;
    uint8 size = strlen(Command);
    uint8 i;

    for(i = 0; i <= size - 1; i++) {
        hash = hash * hashSeed + Command[i];
    }
    return hash;
}

void createHashTable(void) {
    /* Output to debug */
    /* Convert command string to hashcode and making table */

    uint32 hashcode;
    uint8 i;
    uint8 cmdSize = sizeof(CmdArray) / 4; /* 4 is the per item count */

    debugPrint("\r\n", 2);
    sprintf(buf, "/* Hash code table --------------------- */\r\n");
    debugPrint(buf, strlen(buf));

    for(i = 0; i <= cmdSize - 1; i++) {
        hashcode = getHash(CmdArray[i]);

        /* Output each code to SerialCommander */
        sprintf(buf, "#define hs%s \t 0x%08X \r\n", CmdArray[i], hashcode);
        debugPrint(buf, strlen(buf));
    }

    sprintf(buf, "/* End of Hash code table -------------- */\r\n");
    debugPrint(buf, strlen(buf));

    sprintf(buf, "sizeof(CmdArray) = %d\r\n", cmdSize);
    debugPrint(buf, strlen(buf));
    while(1);
}

Now prepare your own command, for example,

/* Const Command array */
const char *CmdArray[]
        = {
           /* Command class *********************************/
           "OPR", "AUTO", "MAN", "CCL", "SD", 
           "SYS", "ERR", "ENQ", "DBG", "PTNNUM",
           
           /* System operation ******************************/
           "RESET", "ABORT",
           
           /* Auto mode operation ***************************/
           "RUN", "RESUME", "STOP", "SET", "TIMEMODE", 
           
           /* Manual mode operation *************************/
           "AGLRST", "SHTDN", "CANPREST", "SENTPREST", "ALLPREST", 
           "CANAGL", "SENTAGL", "PTNTEST", "PTNSTART", "PTNABORT",
           
           /* Cycle operation *******************************/
           "CYCLE", "CCLSPAN", "RPTCCL", "RSMCCL",
           
           /* SD memory settings ****************************/
           "INTVL", "PTNRD", "PTNCLR", "RSMCCL", "CDFILE",
           
           /* System settings operation *********************/
           "VTGMAX", "CURMAX", "TMOUT", "FREQ", "AGLMODE",  
           "SENTCODE", "PRESET", "TIME", "DATE", "RTCOFS",
           "USEACC", "ACCTM", "PIDKP", "PIDKI", "PIDKD", 
           "XMITDATA", "POSTOL", "CANDEVSEL", "SERIAL",
           
           /* Error override operation **********************/
           "SDOVD",
           
           /* System enquiry operation **********************/
           "STATUS", "ASCDATA", "ASCSTATUS", "SYSID", "PTNDAT",
           "SETTINGS", "SRPAGE", "DATA",
           
           /* Debug operation *******************************/
           "LOGON", "MONAGL", "RUNTEST", "CDFILE", "CHGIMG",
           "MOVPOS",
           
           /* Device IDs ************************************/
           "CAN", "SENT", "CTV", "CBV",
           
           /* Auxiliary commands ******************************/
           "LOW", "HIGH", "ON", "OFF", "INCLD", "EXCLD",
           "UNIPOL", "BIPOL",
};

In main(), insert the following.

int main(void) {

    createHashTable(); /* Comment out this line after getting the codes */

    /* Super loop */
    while(true) {

        /* Do Things here */

    }
}

Using debug tool, we might have

/* Hash code table ---------------------- */
#define hsOPR        0xD76C3E81 
#define hsAUTO       0x0FFCEC91 
#define hsMAN        0x9DCCCC64 
#define hsCCL        0x7F973DAA 
#define hsSD         0x020DAC1F 
#define hsSYS        0x4A262267 
#define hsERR        0xB936AFC9 
#define hsENQ        0xB91D5A64 
#define hsDBG        0x9C31213D 
#define hsPTNNUM 0xDC56212A 
#define hsRESET 0x447349B3 
#define hsABORT 0x4CEFC828 
#define hsRUN            0x2D6C940D 
#define hsRESUME 0x97964821 
      ........
/* End of Hash code table --------------- */

Now copy the table from the debugger and paste it to the program. It will be used in the command interpreter.

2018년 6월 4일 월요일

How to parse incoming command from a serial port.

Parsing serial receive buffer

    When an embedded system needs to receive commands from the host or from other devices, we have to identify what the command has been received and how many its arguments exist. Useful string functions in C can be used to fulfill this job and it is very simple.

With the following declarations, the parser function will work.

/**************************************************************/

#define  maxRxBufLength  200
#define  maxParLength  20
#define  maxParCount   20

char rx_data[maxRxBufLength];
char cmdString[maxParCount][maxParLength];
int  rx_count;

int cmdParser(void) {
    /* Extract each command string from rx_data ---------*/
    /* using string functions,  string to token : strtok() ---*/
    /* and string concatenation ------------------------------*/
    /* Returns argument count */

    char *ptr;
    char delimiters[] = ",:;/ \r\n"; // Specify delimiters here
    int parCount = 0;

    // add null at the end of the rx_data
    rx_data[rx_count] = '\0';

    // extract tokens from rx_data
    ptr = strtok(rx_data, delimiters);
    while(ptr != NULL) {
        cmdString[parCount][0] = '\0'; // Make empty string
        // strncat adds Null termination automatically
        strncat(cmdString[parCount++], ptr, maxParLength - 1);

        ptr = strtok(NULL, delimiters);

        /* check if cmdString overflows */
        if(parCount>= maxParCount) {

            break;
        }
    }

    return parCount;
}
/**************************************************************/

Example >

Before calling cmdParser(), the rx_data and the rx_count should be provided by RX interrupt or other means. 

If we have received data in the rx_data like,

rx_data[] = "SET ADCAVG 300 \r\n"

and then call cmdParser() something like

    rxParCount = cmdParser();

The result will be :

rxParCount = 3, 
cmdString[0] = SET\0,
cmdString[1] = ADCAVG\0,
cmdString[2] = 300\0,

2018년 4월 27일 금요일

Serial port Monitoring and Commanding

Serial port Monitoring and Commanding

  PC에서 serial port를 이용해서 embedded system을 control 또는 debugging 하는 것은 개발자 들에게 꼭 필요한 일 입니다. 시중에 몇가지가 있지만 필요한 기능들이 많이 부족하고 그다지 마음에 들지도 않아서 Visual Basic을 이용해서 만들어 봤습니다.  주변에 사용하시는 분들의 조언을 받아 기능이 계속 Upgrade되고 있습니다. free하게 사용 하시면 됩니다. 하지만 error가 있을 수 있습니다. 양해해 주시기 바랍니다. 설치는 필요 없습니다.

  It is quite important for the engineers developing embedded system to control and debug its hardware and firmware. There are some good programs in the market but they are too complex or too simple for me, so I've made mine using Visual Basic. Functions are continuously upgraded referring to my colleagues' opinions. There is no copy right, so download and use freely. There might be errors, please understand. No installation is required.

Click here to download SerialCommander V4.6
Click here to download example command file

1. Features

  1) A group of commands can be transmitted and repeated with specified interval.
  2) List of Commands can be saved and read in.
  3) Work space also can be saved and retrieved automatically when starting.
  4) All pane colors and fonts can be reconfigured by user.
  5) User scratch pad is provided for memo and can be saved and recalled.
  6) Conversion functions for selected text are provided.
     ASCII -> HEX, Integer, Floating point,
     HEX -> ASCII, Integer, Floating point,
  7) Little or Big endian selection
  8) Separate hex monitoring window can be opened.
  9) Header and delimiter selection
  10) ASCII and binary hex mixed command can be transmitted by using '$' sign.
  11) A time stamp can be shown to help monitor an experiment.
  12) File dump and RX logging are provided.
  13) Search text pattern function
  14) All panes are resizable.
  15) Additional 40 more commands can be used.
  16) Multiple application can be opened and numbered.


2. Main Toolbar


File operation
1) Create a new command file
2) Open an existing command file
3) Save current commands in a file
** New command editor : enter new commands and save

1) Comm port select
2) Baudrate select
3) Comm port open|close
                                                     4) Comm port setup



1) Shows ASCII table
2) Invoke windows calulator
                  3) Custom hex converter/calculator


1) Change font and backcolors
2) Save current user workspace
                 3) About + brief usage




              1) TX/RX Transaction flashes ( Red for TX and Green for RX )
              2) Predefined command for abort and reset(format selectable using dropbox)

3. TX pane Toolbar


Clears current display of TX pane


Displays separate HEX monitoring window


Stamps current time in the TX pane


1) Changes current Font
2) Wordwrap On/Off
3) Find text

1) Change Endian : L represents Little, B for Big
2) Convert ASCII to HEX( Use dropdown to select mode
   ( Hex, integer 32 bit, float 32 bit )
                                        The conversion results will be shown in the conversion
                                              results pane.
                                        example : 12500 is selected, and the results

                                     3) Convert HEX to ASCII
                                         The results show each conversion
                                     4) Multiple conversion is allowed( 2 or more selection )



1) Opens file for dump ( Use dropdown to show contents )
2) Send file contents( Dump )

Saves selected text to a file


4. TX pane Statusbar


1) Display mode select : Use dropdown box to select display mode options
2) ASCII mode : Unprintable character option Null, CarriageReturn
3) HEX mode : Show address column - hex | decimal address
                                                   check selected again will make no address
                   Spacing on/off between bytes
                   Bytes/Line select ( 8 or 16 )
4) TXchrs : click to reset character counter
5) Selected : shows how many characters are selected.

5. RX pane Toolbar and Statusbar

  RX pane is identical to the tools in TX except for logging button.

Click to start to log data from RX. Specify file name at start. To stop logging, click this button again.

6. Conversion Results and User Scratch pad

Time stamp and file operations are provided.

7. Group command Column

1) Three group command are provided.
2) Select a group to be transmitted with radio buttons.
3) Specify command interval between each command.
4) Group command can be repeated with specified interval
   and checking Repeat check box.
5) HEX mode can be selected by checking HEX mode.
   HEX commands are like this "02 05 1f....03"
6) Select delimiter trimming( CR/LF )
7) Click send to start group command sequence







8. Immediate command Section


1) A header can be sent concatenated with any command and check or uncheck this function in accordance with your needs.
For example, this can be used for specifying address of RS485.
              2) Immediate Command List contains all possible command with comments.
                 Comments are identified with // or /*  */ or '  or -- to explain the usage.
                 Selected command in the combo box will be transferred to the Send Boxes
                 without its comment.

1) Three immediate command boxes are provided. They can be filled from Immediate Command List selection or be filled with direct typing in.
2) Send button transmits selected one.

1) Options are almost the same as Group commands.
2) Delimiters can be organized with trim and add selection radio buttons.
3) Repeat CMD send the selected with                                                                  specified interval.
               4) More command button(Green +) will provide additional 40 commands.


              5) Repeat command also can be applied.
                 Other options are applied as in the main window.

              6) Edit immediate command file button : all commands can be edited here
                 and can be saved. If you use group and more commands, do not save this
                 as the same file. This is for only the immediate file. Just close this window
                 and use file menu in the main tool bar in the case.


9. Main Statusbar


1) CommStatus indicates port status that is connected or disconnected
2) Command File shows currently opened file name. Click file name to show
   the last portion of a long file name.
3) RXchrs and TXchrs show the number of characters received and transmitted.
4) Selected shows the number of characters selected.

*** Please feedback when you find some errors or inconveniences.


2018년 4월 26일 목요일

PIC Peripheral Pin Select

PIC Peripheral Pin Select(PPS: 주변장치 Pin선택기능)

  PIC24 또는 32 Series 중에는 주변장치 pin을 program에서 select 할 수 있는 기능이 있는 chip들이 있습니다. HW design시 routing이 꼬이지 않도록 하거나 최단거리 배선을 할때 상당히 유용하게 사용할 수 있습니다. 하지만 어느 Pin이나 마음대로 선택, 지정 할 수 는 없고 일정범위 내에서 가능 합니다. 또한 I2C 같은 장치는 고정핀 으로 되어 있으므로 유의 해야 합니다.

  Some processors of PIC24 or PIC32 series has Peripheral Pin Select(PPS) feature and they are very useful when designing Hardware to make routing untwisted or shortest path. But they can not be selected or specified for any pins on the chip and have range of flexibility. Moreover, there are peripherals having fixed pins like I2C, so care must be taken to use this feature.

  다음은 정리한 PIC24FJ128 사용예 입니다. 다른 chip들도 이런 방식으로 정리하고 사용하면 매번 datasheet를 들춰보는 수고를 줄여도 됩니다.


  다음은 PIC32MX350 사용예 입니다.
Click here to download PPS selection example

2018년 4월 20일 금요일

PIC microprocessor selection

PIC microprocessor에 대한 경험을 알려 드리고 선택에 도움이 되었으면 합니다.


    저는 PIC을 접한지 꽤 되었는데 아직도 완벽 하다고는 생각지 않습니다. 너무나 종류도 많고 memory크기, interface도 다양해서 어떤것을 써야 될지 감이 오지 않을 정도 입니다. 하지만 engineer들은 어떻게 해서든지 일을 끝내야 하기 때문에 선택을 해야만 하는 입장 입니다. 
    이제는 경쟁사도 많고 가격도 많이 평준화 되어 대량생산용이 Application이 아니면 가격에 크게 구애받지 않고 spec이 좋은 것을 선택하여 일을 멋지게 끝내는 것도 방법중 하나 일 겁니다. 
    기존 series에 계속해서 upgrade가 되어서 나오는 중이라 저도 어떤것이 좋다고 말씀 드리기 어렵습니다. 뭔가 사용해 보고 익숙해 지면 잘 바꾸지 않는 것이 Engineer들의 특성인 지라 더욱 그렇습니다.

계열 별로 제가 사용해본 경험으로는,

  1. 16F 계열은 싼가격이 장점 이지만 ROM/RAM size가 작아서 기능이 많지 않은 Job에나 적당 할거 같습니다. clock  speed도 높지 않습니다.
  2. 18F 계열은 8bit processor 입니다. 중급 정도 Application에 사용하면 좋습니다. 하지만 ROM/RAM size는 부족한 감이 있습니다. clock speed는 대략 40MHz 정도로 보면 됩니다. RAM은 4K 정도 입니다.
  3. 24FJ 계열은 16bit 입니다. 중 ~ 고급 정도에 적합할거 같습니다. 하지만 RAM size가 32K barrier가 있습니다. 64K를 넘는 종류도 있습니다만 32K를 넘는 부분을 access 하려면 좀 특수한 조작을 해야하는 불편함이 있었습니다. clock은 18F와 거의 비슷한 수준 입니다.  따라서 처리속도 면에서는 18F 보다 훨씬 낫다고 얘기하긴 어려울듯 합니다. Peripheral Pin Select(PPS, 주변장치 Pin mapping)가 지원되는 chip들이 있습니다.
  4. dsPIC 계열은 많이 사용해 보지 않았습니다만 Core는 24F 계열을 기반으로 고속 Clock탑재, control 및 signal processing을 위한 다양한 module들을 용도에 따라 선택 할 수 있습니다.
  5. 32MX 계열은 32bit processor 입니다. ROM/RAM size가 크고 Linear하게 acess 됩니다. Clock speed는 100MHz 정도로 보면 됩니다. Clock에 걸맞게 명령 실행시간 또한 빠릅니다. 풍부한 interface module들이 있습니다. 아마 ARM processor등을 겨냥하여 나온것 같습니다. 사용 하기가 까다롭지 않습니다. 가격이 많이 내려가서 저는 웬만한 application에는 32MX를 사용합니다.

 저는 아래와 같이 Selection table을 만들어서 필요할 때 사용하고 있습니다.


* PU/PD/OD 는 각각 Pullup, Pulldown 및 OpenDrain 입니다.
*  IO bit change time은 port에 0과 1을 교대로 써줄때 측정한 값 입니다.

 Operation time은 evaluation시에 oscilloscope로 실측한 값 입니다. 주변장치 혹은 Host와 통신 시에는 data의 formatting이 필요한 경우가 많아서 그에 대한 speed또한 측정해 보았습니다. 부동소수점 으로 갈 수록 32bit가 위력을 발휘 하는거 같습니다. 앞으로 설명드릴 내용은 주로 PIC32MX350을 위주로 전개해 나갈 것 입니다.


Click here to download Selection Guide


XIDE Settings

XIDE user settings

  XIDE를 Install 후에는 약간의 사용자 setting으로 더욱 유용하게 사용할 수 있습니다.  Editor setting이 내용이 많습니다. Setting이 끝난 후에는 export하여 저장이 가능하고 나중에 새로운version을 설치시 import하여 setting 내용을 유지 할 수 있습니다.

  After installing XIDE, some user setting make it more useful especially for editor. User settings can be exported and can be used when installing newer version by importing the setting contents.

  Tools>Option을 선택 하면 Option 화면이 나타 납니다.


  Click Editor icon, then click formatting tab.


  Select Language as C, then modify settings. Your modification will be shown in the preview pane. Then click Code Templates.


  일종의 단축키 입니다. code작성시 단축형을 입력하고 tab key를 치면 expanded text가 나타 납니다. 아주 유용 합니다. 주로 block형 으로 된 if, for, switch, function등을 자신에게 맞도록 변경하고 사용하면 됩니다. List에 없는 것은 new를 click하고 만들면 됩니다. 이제 Fonts & Colors icon을 click하고 font를 선택 합니다.

  The code templates are sort of shortcut keys. When writing codes enter the abbreviation then press tab key, then the expanded text will appear. It's very useful to build a code structure. Modify some block of codes like - if, for, switch and function - to fit to your needs. If anything needed not in the list, click new button and make your own. Now click Fonts & Colors icon and then select font. 


  Click Team icon and then select Action Items tab.


  ToDo patterns는 Code 에 comment로 삽입해 놓으면 나중에 이를 근거로 해당 부분을 찾을 수 있습니다. ( Window>Action items )

  ToDo patterns can be inserted in the source code as a comment and they could be found with Window>Action items. It reminds what to do at that point.

Click Appearance icon and select look and feel. In my case I selected Nimbus.


  마지막 으로 좌측하단의 Export를 Click 하고 Option에서 all을 check, target zip file을 결정후 OK를 누르면 일반적인 setting이 대강 끝납니다.


  Finally, press export button in the bottom left corner, then the export dialog box will appear. Check All in the options and specify target zip file name and press OK button.