STM8 study notes---How to define the communication protocol in serial communication

Publisher:as8849402Latest update time:2020-01-03 Source: eefocusKeywords:STM8 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

When you first start learning about microcontrollers, serial communication is often used, but in actual products, serial communication requires a communication protocol. Many people don't understand why communication protocols are used, how to define communication protocols, and how to write programs with communication protocols. Today, let's talk about how serial communication protocols are defined.


Let’s first look at the simplest serial port program.


void Uart1_Init( unsigned int baudrate )

{

    unsigned int baud;

    baud = 16000000 / baudrate;

    Uart1_IO_Init(); //IO port initialization

    UART1_CR1 = 0;

    UART1_CR2 = 0;

    UART1_CR3 = 0;

    UART1_BRR2 = ( unsigned char )( ( baud & 0xf000 ) >> 8 ) | ( ( unsigned char )( baud & 0x000f ) );

    UART1_BRR1 = ( ( unsigned char )( ( baud & 0x0ff0 ) >> 4 ) );


    UART1_CR2_bit.REN = 1; //Receive enable

    UART1_CR2_bit.TEN = 1; //Send enable

    UART1_CR2_bit.RIEN = 1; //Receive interrupt enable

}

//Blocking send function

void SendChar( unsigned char dat )

{

    while( ( UART1_SR & 0x80 ) == 0x00 ); //Send data register empty

    UART1_DR = dat;

}

//Receive interrupt function 

#pragma vector = 20            

__interrupt void UART1_Handle( void )

{

    unsigned char res;

    res = UART1_DR;  

    return;

}


There are three main functions: an initialization function, a sending function, and a receiving interrupt. First, define a simple protocol, for example: when a 1 is received, the LED light turns on, and when a 0 is received, the LED light turns off. Then the receiving interrupt function can be modified to:


#pragma vector = 20             

__interrupt void UART1_Handle( void )

{

    unsigned char res;

    res = UART1_DR; 

    if( res == 0x01 )

LED = 1;

else if( res == 0 )

LED = 0;

    return;

}


Directly judge the received data value and control the state of the LED light according to the data value.

What if you need to control two LED lights? You need to send two data to control the status of the LED lights.

Next, we will make the protocol more complicated. The first bit of data controls LED1, and the second bit of data controls LED2. Similarly, 1 turns on the LED, and 0 turns off the LED. If this is the case, when receiving data, we cannot control the LED status after receiving one bit of data, because there are two bits of data to be sent this time, and we must distinguish the first and second bits of data. So we can consider using an array to receive data. Modify the program as follows:


#pragma vector = 20             

unsigned char res[2];

unsigned char cnt=0;

__interrupt void UART1_Handle( void )

{

    res[ cnt++ ] = UART1_DR; 

    if( res[ 0 ] == 0x01 )

LED1 = 1;

else if( res[ 0 ] == 0 )

LED1 = 0;

if( res[ 1 ] == 0x01 )

LED2 = 1;

else if( res[ 1 ] == 0 )

LED2 = 0;

    return;

}


In this way, the received data is stored in an array, and then the subscript is used to determine the data to control the state of the LED light.


If you need to control three LEDs, the data to be sent must be increased by one bit. Adding a third LED, the data can be received in the same way. Modify the program as follows:


#pragma vector = 20             

unsigned char res[3];

unsigned char cnt=0;

__interrupt void UART1_Handle( void )

{

    res[ cnt++ ] = UART1_DR; 

    if( res[ 0 ] == 0x01 )

LED1 = 1;

else if( res[ 0 ] == 0 )

LED1 = 0;

if( res[ 1 ] == 0x01 )

LED2 = 1;

else if( res[ 1 ] == 0 )

LED2 = 0;

if( res[ 2 ] == 0x01 )

LED3 = 1;

else if( res[ 2 ] == 0 )

LED3 = 0;

    return;

}


In this way, even if more LED controls are added, the communication is still applicable. It seems that such a protocol is sufficient for use. But think about it carefully, this protocol seems to have no problem. The only disadvantage is that it will treat each received data as a valid command. If the host computer does not send a command to light up the LED, but there is interference on the serial port line, if the interference signal happens to be 0 or 1, then the program may malfunction. It is necessary to use a flag to determine whether the currently sent data is a real command or interference when receiving data. Use a special value as the beginning of the receiving instruction. Here, 0XA5 is selected as the beginning of the data. Why 0xA5 is selected? Because the binary digits of 0xA5 1010 0101 are just one 1 and one 0, separated. Using this number as the beginning can largely avoid interference signals, because interference signals are generally not such high-low, high-low and very regular signals. So the protocol is changed to 0xA5 as the first data, as a flag for the host computer to send a command, and then 0 and 1 are used to represent the state of the LED. 0 means the LED is off, and 1 means the LED is on. If we need to light up 3 LEDs, the command sent by the host computer is 0xA5 0x01 0x01 0x01, a start mark, followed by three control signals. The program is modified as follows:


#pragma vector = 20             

unsigned char res[4];

unsigned char cnt=0;

__interrupt void UART1_Handle( void )

{

    res[ cnt++ ] = UART1_DR; 

    if( res[ 0 ] == 0xA5 )

    {

    if( res[ 1 ] == 0x01 )

LED1 = 1;

else if( res[ 1 ] == 0 )

LED1 = 0;

if( res[ 2 ] == 0x01 )

LED2 = 1;

else if( res[ 2 ] == 0 )

LED2 = 0;

if( res[ 3 ] == 0x01 )

LED3 = 1;

else if( res[ 3 ] == 0 )

LED3 = 0;

}

    return;

}


In this way, when the first data is received, it is first determined whether it is 0xA5. If it is 0xA5, it means that it is the instruction sent, and the subsequent commands are executed. If the first data is not 0xA5, it means that it is an interference signal, and the command is not executed. In this way, interference signals can be avoided, which may cause the program to malfunction. Is this okay? Careful analysis, if the interference signal does not occur at the beginning of the data, but occurs at the end of the data, for example, I only need to control two LED lights now, the instruction is 0xA5 0x01 0x01, but after receiving the first few data, interference occurs, and the data has an extra 0x01, then the data received by the microcontroller becomes 0xA5 0x01 0x01 0x01, causing the third light to be turned on by mistake. In order to avoid this interference, an end mark can be added to represent the end of sending data, and 0x5A is used as the end, which is exactly the opposite of the start mark. Then if you want to control two lights at this time, the data sent becomes 0xA5 0x01 0x01 0x5A. The code is modified to:


#pragma vector = 20             

unsigned char res[4];

unsigned char cnt=0;

__interrupt void UART1_Handle( void )

{

    res[ cnt++ ] = UART1_DR; 

    if( ( res[ 0 ] == 0xA5 )&&( res[ 3 ] == 0x5A ) )

    {

    if( res[ 1 ] == 0x01 )

LED1 = 1;

else if( res[ 1 ] == 0 )

LED1 = 0;

if( res[ 2 ] == 0x01 )

LED2 = 1;

else if( res[ 2 ] == 0 )

LED2 = 0;

}

    return;

}


In this way, by simultaneously judging the start and end flags of the sent data, it is ensured that the received data is a real command and data interference is avoided. However, after careful observation, a new problem was discovered. The data position subscript of the end flag is fixed, which means that only 4 bytes can be sent each time data is sent, which means that only two LED lights can be controlled each time. If you want to increase the number of controlled LED lights, you have to modify the program. This is very inconvenient in actual operation. Can you dynamically identify how many data have been sent? So I thought that when sending instructions, I tell the microcontroller that I want to control the number of LEDs. The microcontroller automatically determines how many LED lights need to be lit according to the number value. Therefore, the protocol is modified to add another bit after the start flag, representing the number of LED lights to be controlled, followed by the light or extinguish command, and finally the end flag. If you want to light up 2 LED lights now, the data sent is: 0xA5 0x02 0x01 0x01 0x5A, and the 0x02 after the start flag means that two LED lights are to be controlled. If you want to light up 3 lights, the data sent is 0xA5 0x03 0x01 0x01 0x01 0x5A. So how do you determine the position of the end mark? By observing the above two sets of data, we can find that if we control 2 LED lights, the end mark is at the 4th position, and if we control 3 LED lights, the end mark is at the 5th position. The position of the end mark is just 2 more than the number of controlled LED lights, so the program is modified as follows:


#pragma vector = 20             

unsigned char res[5];

unsigned char cnt=0;

unsigned char num=0;

__interrupt void UART1_Handle( void )

{

    res[ cnt++ ] = UART1_DR; 

    if( res[ 0 ] == 0xA5 )

    {

    num = res[1];

    if( res [ num + 2] == 0x5A )

    {

        if( num == 3 ) 

        {

    if( res[ 2 ] == 0x01 )

LED1 = 1;

else if( res[ 2 ] == 0 )

LED1 = 0;

if( res[ 3 ] == 0x01 )

LED2 = 1;

else if( res[ 3 ] == 0 )

LED2 = 0;

if( res[ 4 ] == 0x01 )

LED2 = 1;

else if( res[ 4 ] == 0 )

LED2 = 0;

}

  if( num == 2 ) 

  {

    if( res[ 2 ] == 0x01 )

LED1 = 1;

else if( res[ 2 ] == 0 )

LED1 = 0;

if( res[ 3 ] == 0x01 )

LED2 = 1;

else if( res[ 3 ] == 0 )

LED2 = 0;

}

}

}

    return;

}

By adding a quantity judgment to the protocol, the program can dynamically set the state of the LED light. However, I feel that there are too many codes in the serial port interrupt. Data reception and data processing are placed in one function, which makes the program difficult to read. Can the data reception and data processing be separated? Then you can only receive data in the serial port interrupt function. Set the flag bit after the data is received, and then process the received data in the main function. So modify the program to:


#pragma vector = 20             

unsigned char res[5];

unsigned char cnt = 0;

unsigned char num = 0;

unsigned char receive_ok = 0;

__interrupt void UART1_Handle( void )

[1] [2]
Keywords:STM8 Reference address:STM8 study notes---How to define the communication protocol in serial communication

Previous article:STM8 learning notes---How to determine the register name in the program
Next article:STM8L RTC Summary (2) Initialization and Configuration

Recommended ReadingLatest update time:2024-11-16 11:45

Some minor problems encountered with STM8
1. Compiler optimization issues If the defined local variables are not used later, they will be optimized by the compiler, such as: {      u32 va = 100;     va = 200;   //Put breakpoints at some places and check the value of va, you will find that it is not correct at all. } How to avoid optimization? {
[Microcontroller]
Some minor problems encountered with STM8
Implementing similar bit-band operations in STM8 IAR [compatible library functions]
I have been using STM8 for a while and found that the "iostm8xxx.h" that comes with IAR and the standard library of ST cannot coexist. So I combined the IAR library and imitated the "sys.h" in the Zhengdian Atom STM32 routine to write "stm8_sys.h", which is convenient for myself and others. Thank you for your support!
[Microcontroller]
STM8 series MCU timer operation
1. void TIM1_DeInit(void) //Reset all register parameters of TM1 2. Enable the clock CLK_PeripheralClockConfig (CLK_Peripheral_TIM1, ENABLE) used by TIM_1 3. Set the timer period Function void TIM1_TimeBaseInit(uint16_t TIM1_Prescaler,                        TIM1_CounterMode_TypeDef TIM1_CounterMode,                  
[Microcontroller]
Common problems and solutions for STM8 microcontrollers
Simulation error error30220 When using STVD software to simulate a program, sometimes an error occurs and a warning window of error 30220 pops up.       The reason for this problem may be that the STVP software has just burned the program, and the STLINK is occupied by the burning software. At this time, you can cl
[Microcontroller]
Common problems and solutions for STM8 microcontrollers
STM8 new project standards in IAR environment
1. The first step is to create a new folder and name it related to your project, and create a Project directory under this folder. 2. Enter the Project directory and create a new EWSTM8 directory 3. Open IAR, Project- Create New Project 4. Select Empty project 5. Save the project file in XXX project ProjectEWS
[Microcontroller]
STM8 new project standards in IAR environment
STM8 time base unit
STM8 time base unit The time base unit contains: 16-bit up/down counter  16-bit auto-reload register Repeat counter Prescaler (Figure 29: STM8 time base unit) The 16-bit counter, prescaler, auto-reload register and repeat counter register can be read and written by software. The auto-reload register consist
[Microcontroller]
STM8 time base unit
A brief discussion of I2C bus (IV) -- Example of IIC driver file for STM8
This example provides the IIC driver file source code of the STM8S103F core board. It has no problems after personal testing. The blogger uses it to drive AT24C256 and electronic compass MMC5883MA. The IIC communication rate is 100kHz, and the timing delay time can be adjusted. Paste the source code directly: IIC.
[Microcontroller]
IAR+STM8——GPIO
On the second day, we started learning from the most basic IO operations. Most of the IO on STM are GPIO. When you are just starting to learn, don't make the test program complicated. The simpler it is, the less likely it is to make mistakes. The following is the code, without using the STM8 official firmware library.
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components
Guess you like

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号