[Orangepi Zero2 Allwinner H616] Driver serial communication

1. Basic understanding of serial port
2. Serial port electrical standards and protocol standards
3. Serial communication protocol
4. Using serial port communication in Linux system
5. orangepi zern2 two sets of serial ports
6. Serial test serialtest

  • Modify the configuration file according to the manual
  • According to the results, we know that the code sends ASCII data through the serial port.

7. Serial port development based on wiringPi
8. Serial communication achieved by analyzing source code

1. Basic understanding of serial ports

Serial Port, also known as a serial port, is a physical interface used for data communication, allowing data to be transmitted between a computer and external devices in the form of a continuous bit stream. Serial communication is very common in a variety of applications, including sensor connections, embedded systems, communication devices, GPS receivers, printers, and more.

The following are some basic knowledge about serial ports:

  1. Features of the serial port:

    • Serial communication is a type of point-to-point communication, usually used to connect a computer and an external device.
    • Data is transmitted bit by bit (bit), at specific time intervals.
    • Serial communication can use different baud rates (communication rates). Commonly used baud rates include 9600, 115200, etc.
  2. Standards for serial communication:

    • Serial communication usually follows some standards, such as RS-232, RS-485, UART, etc. These standards specify communication parameters such as signal level, number of data bits, and parity bits.
    • RS-232 is a common serial communication standard that defines the electrical and mechanical characteristics of serial communication.
  3. Serial communication parameters:

    • Serial communication requires configuration parameters to ensure consistency between communication parties. These parameters include baud rate, data bits, stop bits, parity bits, etc.
    • The baud rate represents the number of bits transmitted per second, the data bits represent the number of bits per byte, the stop bits specify the stop interval between bytes, and the check bit is used to detect the integrity of the data.
  4. Data transfer:

    • Serial communication divides data into bytes and then transmits them bit by bit. Typically, each byte begins with a start bit, followed by data bits, followed by optional parity and stop bits.
  5. Flow control:

    • Serial communication can use hardware flow control (RTS/CTS) or software flow control (XON/XOFF) to manage data flow. Flow control is used to prevent data overflow.
  6. Serial device:

    • Serial communication requires the use of a serial device, usually connected to the computer through a serial port (such as a COM port). In Linux systems, serial port devices are usually located in the /dev directory.
  7. Application:

    • Serial communication is widely used in various fields, including data acquisition, embedded system communication, sensor connection, external device control, GPS data reception, etc.
  8. Programming:

    • In programming, you can use a suitable programming language (such as C/C++) and serial port library for serial communication. Typically, you need to open a serial device, configure communication parameters, and read and write data.

Serial communication is a very useful communication method, especially suitable for connecting external devices and embedded systems. Understanding the basic concepts and parameter settings of serial communication is very important for successful serial communication.

2. Serial port electrical standards and protocol standards

Serial communication involves two main aspects of standards: electrical standards and protocol standards. Electrical standards specify the signal levels and electrical characteristics of the physical layer, while protocol standards specify the format and transmission method of data. Here are some common standards for both:

Electrical standards:

  1. RS-232: RS-232 is a common serial electrical standard that defines the electrical and mechanical characteristics of serial communications. The RS-232 interface uses positive and negative levels to represent logical values and is commonly used to connect computers and external devices.

  2. RS-485: RS-485 is a multipoint, half-duplex serial port standard commonly used in industrial control and data communications applications. RS-485 has longer transmission distance and higher noise immunity.

  3. TTL (Transistor-Transistor Logic): TTL is a digital level standard commonly used in embedded systems and digital circuits. TTL level usually represents logic low level with 0V, and logic high level with 3.3V or 5V.

  4. CMOS (Complementary Metal-Oxide-Semiconductor): CMOS is also a digital level standard commonly used in digital circuits and microcontrollers. The CMOS level usually represents a logic low level with 0V, and a logic high level with a value close to the power supply voltage.

  5. LVDS (Low Voltage Differential Signaling): LVDS is a differential signaling standard commonly used for high-speed data transmission, such as monitor connections.

Protocol standards:

  1. UART (Universal Asynchronous Receiver/Transmitter): UART is a common serial communication protocol, usually used for asynchronous transmission of data. It includes start bits, data bits, stop bits and optional parity bits.

  2. SPI (Serial Peripheral Interface): SPI is a synchronous serial communication protocol commonly used to connect microcontrollers, sensors and peripherals. SPI uses a master-slave structure that includes clock, data input, and data output lines.

  3. I2C (Inter-Integrated Circuit): I2C is a serial communication protocol usually used to connect multiple devices to the same bus. It uses a two-wire system, including data line (SDA) and clock line (SCL).

  4. RS-232, RS-485 protocols: In addition to electrical standards, RS-232 and RS-485 include communication protocols that define data frame format and flow control.

  5. Modbus: Modbus is a serial communication protocol commonly used in industrial automation and supports multiple physical layer standards, including serial communication.

  6. CAN (Controller Area Network): CAN is a serial communication protocol commonly used in automotive and industrial control fields. It supports reliable communication in high-noise environments.

  7. USB (Universal Serial Bus): USB is a universal serial communication standard used to connect computers and external devices. It supports high-speed, full-speed and low-speed transmission modes.

Different applications and hardware requirements often require different electrical and protocol standards. Therefore, when performing serial communication, you need to understand the specifications and requirements of the device you are using in order to correctly configure the serial port parameters and select the appropriate communication protocol.

3. Serial communication protocol

Parameters such as baud rate, parity bit and stop bit in the serial communication protocol are very important. They determine the data transmission method and data integrity check. The following are some common serial communication parameters:

  1. Baud Rate:

    • Baud rate is the transmission rate in serial communication, which represents the number of bits transmitted per second. Common baud rates include 9600, 115200, 57600, etc.
    • Both parties to the communication must use the same baud rate. A baud rate that is too high may cause data transmission errors, so an appropriate baud rate needs to be selected based on the hardware and communication distance.
  2. Data Bits:

    • Data bits specify the number of bits in each data byte, usually 5, 6, 7, or 8 bits. In most cases, 8 data bits are used to support 8-bit binary data.
  3. Parity:

    • The parity bit is used to detect errors in data transmission. Usually there are the following options:
      • No parity digit: No parity digit is used.
      • Odd parity bit: Ensures there is an odd number of “1”s in the data bits.
      • Even parity bit: Ensures that there is an even number of “1”s in the data bits.
    • Parity bits are typically used to detect single-bit errors.
  4. Stop Bits:

    • The stop bit indicates the end of the data byte. There are usually 1-bit and 2-bit stop bit options, with 1-bit stop bit being the most common choice.

These parameters are usually combined together to determine the frame format of the data. For example, a common serial communication setup is:

  • Baud rate: 9600
  • Data bits: 8
  • No parity bit
  • 1 stop bit

This means that each data frame consists of 10 bits: 1 start bit, 8 data bits, no parity bit, and 1 stop bit.

Correctly configuring these parameters is very important for serial communication, because both parties to the communication must use the same parameters, otherwise data transmission errors will occur. Typically, the specifications of a serial device indicate the required communication parameter settings. In programming, you need to use the corresponding library functions to set these parameters to ensure correct data transmission and verification.

4. Using serial communication in Linux system

Serial communication is a communication method that transmits data through a serial connection. It is usually used to connect computers and external devices, such as sensors, embedded systems, GPS receivers, microcontrollers, etc. In Linux systems, you can use serial communication to exchange data with these devices.

The following are the general steps for using serial port communication in Linux systems:

  1. Open serial device:

    • In Linux systems, serial port devices are usually located in the /dev directory. For example, /dev/ttyS0 represents the first serial port device. You can use the open function in C language to open the serial device file, which is similar to opening a file.
  2. Configure serial port parameters:

    • Use the termios structure and the tcsetattr function to configure serial communication parameters, such as baud rate, data bits, stop bits, check bits, etc. These parameters need to match the target device to ensure proper communication.
  3. Reading and writing data:

    • Use the read function to read data from the serial port, and use the write function to write data to the serial port. You can use these functions to exchange data with the device.
  4. Close the serial port:

    • When communication ends, use the close function to close the serial device.

The following is a sample C program that demonstrates how to open a serial port, configure serial port parameters, read and write data in a Linux system:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>

int main() {<!-- -->
    int fd;
    struct termios serial;

    //Open the serial device
    fd = open("/dev/ttyS0", O_RDWR);
    if (fd == -1) {<!-- -->
        perror("open");
        return 1;
    }

    //Configure serial port parameters
    tcgetattr(fd, & amp;serial);
    serial.c_cflag = B9600; // baud rate 9600
    serial.c_cflag |= CS8; // 8-bit data bits
    serial.c_cflag & amp;= ~PARENB; // No parity check
    tcsetattr(fd, TCSANOW, & amp;serial);

    char data_to_send[] = "Hello, Serial!";
    write(fd, data_to_send, strlen(data_to_send));

    char buffer[100];
    ssize_t n = read(fd, buffer, sizeof(buffer));
    if (n > 0) {<!-- -->
        buffer[n] = '\0';
        printf("Received data: %s\\
", buffer);
    }

    close(fd); // Close the serial port

    return 0;
}

This example demonstrates how to perform basic data exchange with a serial device. Please note that the configuration parameters of the serial device need to match the device you are connecting to, so adjust the baud rate and other parameters according to the requirements of the device. This example uses /dev/ttyS0, you may need to specify the correct serial device based on your system and hardware configuration.

5. orangepi zern2 two sets of serial ports


6. Serial test serialtest

/*
 * serialTest.c:
 * Very simple program to test the serial port. Expects
 * the port to be looped back to itself
 *
 * Copyright (c) 2012-2013 Gordon Henderson. <[email protected]>
 *************************************************** *********************
 * This file is part of wiringPi:
 * https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 * wiringPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * wiringPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with wiringPi. If not, see <http://www.gnu.org/licenses/>.
 *************************************************** *********************
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <wiringPi.h>
#include <wiringSerial.h>

int main()
{<!-- -->
  int fd;
  int count;
  unsigned int nextTime;

  if ((fd = serialOpen ("/dev/ttyS2", 115200)) < 0)
  {<!-- -->
    fprintf (stderr, "Unable to open serial device: %s\\
", strerror (errno)) ;
    return 1;
  }

  if (wiringPiSetup () == -1)
  {<!-- -->
    fprintf (stdout, "Unable to start wiringPi: %s\\
", strerror (errno)) ;
    return 1;
  }

  nextTime = millis () + 300;

  for (count = 0; count < 256; )
  {<!-- -->
    if (millis () > nextTime)
    {<!-- -->
      printf ("\\
Out: =: ", count) ;
      fflush (stdout);
      serialPutchar (fd, count);
      nextTime + = 300;
       + + count;
    }

    delay(3);

    while (serialDataAvail (fd))
    {<!-- -->
      printf (" -> =", serialGetchar (fd)) ;
      fflush (stdout);
    }
  }

  printf ("\\
") ;
  return 0;
}

Modify the configuration file according to the manual



Based on the results, we know that the code sends ASCII data through the serial port


7. Serial port development based on wiringPi

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
 
int fd;
 
void* Sendhandler()
{<!-- -->
    char *sendBuf;
    sendBuf = (char *)malloc(32*sizeof(32));
 
    while (1){<!-- -->
        memset(sendBuf, '\0', 32);
        scanf("%s", sendBuf);
        while (*sendBuf){<!-- -->
            serialPutchar(fd, *sendBuf + + );
        }
    }
}
 
void* Revhandler()
{<!-- -->
    while (1){<!-- -->
        while (serialDataAvail(fd)){<!-- -->
            printf("%c", serialGetchar(fd));
            fflush(stdout);
        }
    }
}
 
int main()
{<!-- -->
    int count;
    unsigned int nextTime;
 
    pthread_t idSend;
    pthread_t idRev;
 
    if ((fd = serialOpen("/dev/ttyS5", 115200)) < 0){<!-- -->
        fprintf(stderr, "Unable to open serial device: %s\\
", strerror(errno));
        return 1;
    }
    pthread_create( & idSend, NULL, Sendhandler, NULL);
    pthread_create( & idRev, NULL, Revhandler, NULL);
 
    if (wiringPiSetup() == -1){<!-- -->
        fprintf(stdout, "Unable to start wiringPi: %s\\
", strerror(errno));
        return 1;
    }
 
    while (1){<!-- -->sleep(10);}
 
    printf("\\
");
    return 0;
}

8. Serial communication achieved by analyzing source code

Source code

/*
 * wiringSerial.c:
 * Handle a serial port
 *************************************************** *********************
 * This file is part of wiringPi:
 * https://projects.drogon.net/raspberry-pi/wiringpi/
 *
 * wiringPi is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * wiringPi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with wiringPi. If not, see <http://www.gnu.org/licenses/>.
 *************************************************** *********************
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "wiringSerial.h"

/*
 * serialOpen:
 * Open and initialize the serial port, set all the right
 * port parameters - or as many as are required - hopefully!
 *************************************************** *******************************
 */

int serialOpen (const char *device, const int baud)
{<!-- -->
  struct termios options;
  speed_t myBaud;
  int status, fd;

  switch (baud)
  {<!-- -->
    case 50: myBaud = B50 ; break ;
    case 75: myBaud = B75 ; break ;
    case 110: myBaud = B110; break;
    case 134: myBaud = B134; break;
    case 150: myBaud = B150; break;
    case 200: myBaud = B200; break;
    case 300: myBaud = B300; break;
    case 600: myBaud = B600; break;
    case 1200: myBaud = B1200; break;
    case 1800: myBaud = B1800; break;
    case 2400: myBaud = B2400; break;
    case 4800: myBaud = B4800; break;
    case 9600: myBaud = B9600; break;
    case 19200: myBaud = B19200; break;
    case 38400: myBaud = B38400; break;
    case 57600: myBaud = B57600; break;
    case 115200: myBaud = B115200; break;
    case 230400: myBaud = B230400; break;
    case 460800: myBaud = B460800; break;
    case 500000: myBaud = B500000; break;
    case 576000: myBaud = B576000 ; break ;
    case 921600: myBaud = B921600; break;
    case 1000000: myBaud = B1000000 ; break ;
    case 1152000: myBaud = B1152000; break;
    case 1500000: myBaud = B1500000 ; break ;
    case 2000000: myBaud = B2000000; break;
    case 2500000: myBaud = B2500000 ; break ;
    case 3000000: myBaud = B3000000; break;
    case 3500000: myBaud = B3500000; break;
    case 4000000: myBaud = B4000000; break;

    default:
      return -2;
  }

  if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
    return -1 ;

  fcntl (fd, F_SETFL, O_RDWR);

// Get and modify current options:

  tcgetattr (fd, & amp;options);

    cfmakeraw ( & amp;options) ;
    cfsetispeed ( & amp;options, myBaud) ;
    cfsetospeed ( & amp;options, myBaud) ;

    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag & amp;= ~PARENB ;
    options.c_cflag & amp;= ~CSTOPB ;
    options.c_cflag & amp;= ~CSIZE ;
    options.c_cflag |= CS8;
    options.c_lflag & amp;= ~(ICANON | ECHO | ECHOE | ISIG);
    options.c_oflag & amp;= ~OPOST ;

    options.c_cc [VMIN] = 0;
    options.c_cc [VTIME] = 100; // Ten seconds (100 deciseconds)

  tcsetattr (fd, TCSANOW, & amp;options);

  ioctl (fd, TIOCMGET, & amp;status);

  status |= TIOCM_DTR ;
  status |= TIOCM_RTS ;

  ioctl (fd, TIOCMSET, & amp;status);

  usleep (10000); // 10mS

  return fd;
}


/*
 * serialFlush:
 * Flush the serial buffers (both tx & amp; rx)
 *************************************************** *******************************
 */

void serialFlush (const int fd)
{<!-- -->
  tcflush (fd, TCIOFLUSH);
}


/*
 * serialClose:
 * Release the serial port
 *************************************************** *******************************
 */

void serialClose (const int fd)
{<!-- -->
  close(fd);
}


/*
 * serialPutchar:
 * Send a single character to the serial port
 *************************************************** *******************************
 */

void serialPutchar (const int fd, const unsigned char c)
{<!-- -->
  int ret;
  ret = write (fd, & amp;c, 1);
  if (ret < 0)
  printf("Serial Putchar Error\\
");
}


/*
 * serialPuts:
 * Send a string to the serial port
 *************************************************** *******************************
 */

void serialPuts (const int fd, const char *s)
{<!-- -->
int ret;
ret = write (fd, s, strlen (s));
if (ret < 0)
printf("Serial Puts Error\\
");
}

/*
 * serialPrintf:
 * Printf over Serial
 *************************************************** *******************************
 */

void serialPrintf (const int fd, const char *message, ...)
{<!-- -->
  va_list argp;
  char buffer [1024];

  va_start (argp, message);
    vsnprintf (buffer, 1023, message, argp);
  va_end (argp);

  serialPuts (fd, buffer);
}


/*
 * serialDataAvail:
 * Return the number of bytes of data avalable to be read in the serial port
 *************************************************** *******************************
 */

int serialDataAvail (const int fd)
{<!-- -->
  int result;

  if (ioctl (fd, FIONREAD, & amp;result) == -1)
    return -1 ;

  return result ;
}


/*
 * serialGetchar:
 * Get a single character from the serial device.
 * Note: Zero is a valid character and this function will time-out after
 * 10 seconds.
 *************************************************** *******************************
 */

int serialGetchar (const int fd)
{<!-- -->
  uint8_t x;

  if (read (fd, & amp;x, 1) != 1)
    return -1 ;

  return ((int)x) & amp; 0xFF ;
}

uartTool.h

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "wiringSerial.h"

int my_serialOpen (const char *device, const int baud);

void my_serialSendstring (const int fd, const char *s);

int my_serialGetstring (const int fd, char *buffer);

uartTool.c

#include "wiringSerial.h"
#include "uartTool.h"

int my_serialOpen (const char *device, const int baud)
{<!-- -->
struct termios options; // Create a termios structure for serial port parameter settings
speed_t myBaud; // Create a speed type variable myBaud, used to save the baud rate
int status, fd; // Create integer type variables status and fd, used to save status and file descriptors
 
switch (baud){<!-- --> //Select the appropriate baud rate constant based on the incoming baud rate parameter.
case 9600: myBaud = B9600; break;
case 115200: myBaud = B115200; break;
}
if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1) // Open the serial device and set the opening options
return -1 ; // If the opening fails, return error code -1
\t
fcntl (fd, F_SETFL, O_RDWR); // Set file status flag
\t
// Get and modify current options: Get and modify the current serial port parameters:
 
tcgetattr (fd, & amp;options); // Get the current serial port parameters
 
cfmakeraw ( & amp;options) ; // Initialize the termios structure to original mode
cfsetispeed ( & amp;options, myBaud) ; // Set the input baud rate
    cfsetospeed ( & amp;options, myBaud) ; // Set the output baud rate
 
    options.c_cflag |= (CLOCAL | CREAD); // Local connection and enable reception
    options.c_cflag & amp;= ~PARENB ; // Disable parity checking
    options.c_cflag & amp;= ~CSTOPB ; // 1 stop bit
    options.c_cflag & amp;= ~CSIZE ; // Clear data bit settings using data bit mask
    options.c_cflag |= CS8; // Set 8-bit data bits
options.c_lflag & amp;= ~(ICANON | ECHO | ECHOE | ISIG); // Disable canonical input
options.c_oflag & amp;= ~OPOST ; // Disable output processing
 
options.c_cc [VMIN] = 0; // Minimum number of characters to read data
options.c_cc [VTIME] = 100; // Ten seconds (100 deciseconds) timeout waiting time (one-tenth of a second 100ms)
 
tcsetattr (fd, TCSANOW, & amp;options); //Set new serial port parameters
 
ioctl (fd, TIOCMGET, & amp;status); // Get serial port control mode status
 
status |= TIOCM_DTR; //Set the DTR (data terminal ready) bit
status |= TIOCM_RTS; //Set the RTS (request to send) bit
 
ioctl (fd, TIOCMSET, & amp;status); //Set the serial port control mode status
\t
usleep (10000) ; // Pause for 10 milliseconds
\t
return fd; // Return the serial port file descriptor
}

void my_serialSendstring (const int fd, const char *s)
{<!-- -->
int ret;

ret = write (fd, s, strlen (s));
\t
if (ret < 0)
printf ("Serial Sendstring Error\\
") ;
}

int my_serialGetstring (const int fd, char *buffer)
{<!-- -->
int n_read;

n_read = read (fd, buffer, 32);
\t
return n_read;
}

uartTest.c

#include <pthread.h>
#include "uartTool.h"

int fd;
 
void* readSerial ()
{<!-- -->
    char buffer [32];

    while (1) {<!-- -->
        memset (buffer, '\0', sizeof(buffer)) ;
        my_serialGetstring (fd, buffer);
        printf ("GET->%s\\
", buffer) ;
    }
}
 
void* sendSerial()
{<!-- -->
    char buffer [32];

    while (1) {<!-- -->
memset (buffer,'\0', sizeof(buffer));
    scanf ("%s", buffer) ;
    my_serialSendstring (fd, buffer);
    }
}
 
int main (int argc, char **argv)
{<!-- -->
    char deviceName [32] = {<!-- -->'\0'};
    pthread_t readt;
    pthread_t sendt;
 
    if (argc < 2) {<!-- -->
        printf ("uage:%s /dev/ttyS?\\
", argv[0]) ;
        return -1 ;
    }
 
    strcpy (deviceName, argv[1]) ;
 
    if ((fd = my_serialOpen (deviceName, 115200)) == -1) {<!-- -->
        printf ("open %s error\\
", deviceName) ;
        return -1;
    }
 
    pthread_create (&readt, NULL, readSerial, NULL);
    pthread_create (&sendt, NULL, sendSerial, NULL);
 
    while (1) {<!-- -->sleep (10);}
 
}
gcc uartTool.c uartTool.h uartTest.c -lpthread
./a.out /dev/ttyS5