ARDUINO learning - Communication

Introduction to serial communication

Reference article (written by the boss is very good, you can go and have a look)
The serial communication supported by arduino includes UART,I2C and SPI
According to the transmission direction of serial data, we can divide communication into simplex, half duplex and duplex
Simplex
It means that data transmission can only be in one direction, and reverse transmission cannot be realized
Half duplex
It means that data can be transmitted in two directions, but not at the same time
full duplex
It means that data can be transmitted in both directions at the same time

Hardware serial communication (UART) -- hardware serial class library

In addition to the common functions, there are other commonly used functions
peek()
Function: returns 1 byte of data, but does not delete the data from the receiving buffer. Unlike the read() function, the read() function will delete the data from the receiving buffer after reading the function.
write()
Function: output data to serial port. It is output to the serial port in byte form. The difference between it and print() is that when print() is used to send a data, arduino does not send the data itself, but converts the data into characters, and then sends the ASCII code corresponding to the characters. When the serial port monitor receives the ASCII code, it will display the corresponding characters. Therefore, print() is used Function is to output data to the serial port in the form of ASCII code; When using the write() function, arduino sends the value itself. However, after receiving the data, the serial port monitor will display the corresponding characters as ASCII code.
For example, when using serial.write(INT) to output an integer number 123, the displayed character is "{", because the character corresponding to ASCII code 123 is "{"

Software analog serial communication -- the use of software serial class library

In addition to the HardwareSerial class library, arduino also provides a software erial class library, which can simulate other digital pins into serial communication pins through programs
Generally, the serial port built in arduino is called hardware serial port, and the serial port simulated by software serial class library is called software analog serial port
Sofaweserial class library member function
The defined member functions are similar to those of the hardware serial port
available(), begin(), read(), write(), print(), println(), peek(), the function usage is the same
In addition, the soft serial port has the following member functions
SofaWareSerial()
Function: This is the constructor of software serial class, which can specify RX and TX pins of soft serial port
Syntax: softwaresarial myserial = softwaresarial (rxpin, txpin)
listen()
Function: enable soft serial port monitoring status
arduino can only monitor one soft serial port at a time. When you need to monitor a serial port, you need to call this function on the object to enable the monitoring function
overflow()
Function: detect whether the buffer has overflowed. The soft serial port buffer can store up to 64B of data

experiment

Using UART communication mode requires two parts of rx-tx and TX-RX connection
Two arduino s realize communication, one uno and one Mega. The UNO end is connected to LCD 1602 to display the communication class content
mega's procedure is as follows:

String device_mega = "";
String device_uno = "";
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  if(Serial.available()>0){
    if(Serial.peek() != '\n')
      device_mega += (char)Serial.read();
    else{
      Serial.read();
      Serial.print("you said: ");
      Serial.println(device_mega);
      Serial1.println(device_mega);
      device_mega = "";
    }
  }

  if(Serial1.available()>0){
    if(Serial.available()>0){
      if(Serial1.peek() != '\n')
        device_uno += (char)Serial1.read();
      else{
        Serial1.read();
        Serial.print("the uno said: ");
        Serial.println(device_uno);
        device_uno = "";
      }
    }
  }
}

uno's procedure is as follows

#include "LiquidCrystal.h"
#include "SoftwareSerial.h"
SoftwareSerial myserial(10,11);
String device_mega = "";
String device_uno = "";
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  myserial.begin(9600);
  myserial.listen();
  lcd.begin(16,2);
  lcd.clear();
  
}

void loop() {
  // put your main code here, to run repeatedly:
  if(softSerial.available()>0){
    delay(100);
    lcd.clear();
    while(Serial.available()>0)
      lcd.write(Serial.read());
    device_mega = "";
  }
  if(Serial.available()>0){
	if(softSerial.peek() != '\n')
		device_uno += (char)Serial.read();
	else
	{
	Serial.read();
	Serial.print("you said:");
	Serial.println(device_uno);
	device_uno = "";
	}
}
}

I2C protocol

Using IIC protocol, arduino can connect up to 128 IIC slave devices through two bidirectional bus data lines SDA and clock line SCL.
Different from the one-to-one communication mode of serial communication, bus communication is usually divided into host and slave. During communication, the host is responsible for starting and terminating data transmission, and also outputting clock signal; the slave is addressed by the host and responds to the communication request of the host; in IIC communication, the control of communication rate is completed by the host, and the host will output clock signal through SCL pin for the master All slaves on the line are used; at the same time, IIC is a half duplex communication mode

modelSDASCL
UNOA4A5
MEGA2021

Note that A4 and A5 must not be the pins marked SDA and SCL
The strength of arduino is that it has various encapsulated libraries, which is easy for beginners to use

Wire class library

begin()
Function: initialize the II connection and join the IIC bus as a master or slave device
begin(address)
When the parameter is not filled in, the device will join the IIC bus in the master mode; when the parameter is filled in, the device will join the IIC bus in the slave mode, and the address can be set to any address from 0 to 127
requesFrom()
Function: the master sends a data request signal to the slave
After using requestprom(), at the extreme, you can register an event with onReceive() to respond to the host's request; the host can read the data through the available() and read() functions
beginTransmission()
Function: set to transmit data to the specified slave device.
wire.beginTransmission(address)
endTransmission()
Function: end data transmission
onReceive()
This function can register an event at the slave end and is triggered when the slave receives the data sent by the host
onRequest()
Register an event that is triggered when the host receives a data request from the slave

experiment

The host sends data, the slave receives data and the slave sends data, and the host receives data
Host part:

#include <Wire.h>
// this test is for mega
void setup() {
  // put your setup code here, to run once:
  Wire.begin();

}
byte com = 0;

void loop() {
  // put your main code here, to run repeatedly:
  Wire.beginTransmission(4);
  Wire.write("com is ");
  Wire.write(com);
  Wire.endTransmission();
  com ++;
  delay(500);
}

Slave part

#include <Wire.h>
// this is for uno 4
void setup() {
  // put your setup code here, to run once:

  Wire.begin(4);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(500);

}

void receiveEvent(int howMany){
  while(Wire.available() > 1){
    char c = Wire.read();
    Serial.print(c);
  }
  int com = Wire.read();
  Serial.println(com);
}

Upload the above two programs to mega and uno respectively to realize the communication between the two boards
(2) Receive data from the machine sending data host

// Wire Master Reader
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Reads data from an I2C/TWI slave device
// Refer to the "Wire Slave Sender" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  Wire.requestFrom(8, 6);    // request 6 bytes from slave device #8

  while (Wire.available()) { // slave may send less than requested
    char c = Wire.read(); // receive a byte as character
    Serial.print(c);         // print the character
  }

  delay(500);
}

Slave

// Wire Slave Sender
// by Nicholas Zambetti <http://www.zambetti.com>

// Demonstrates use of the Wire library
// Sends data as an I2C/TWI slave device
// Refer to the "Wire Master Reader" example for use with this

// Created 29 March 2006

// This example code is in the public domain.


#include <Wire.h>

void setup() {
  Wire.begin(8);                // join i2c bus with address #8
  Wire.onRequest(requestEvent); // register event
}

void loop() {
  delay(100);
}

// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
  Wire.write("hello "); // respond with message of 6 bytes
  // as expected by master
}

experiment

The advantage of IIC bus is that it can control multiple slaves simultaneously with only two buses,
iic controlled steering gear
Reference article: link
pca9865 module
Connection:
A4--------SDA
A5--------SCL
5V--------VCC
GND-------GND
adafruit pwm library is required to use PCA9865 module
The steering gear has a control frequency of 50HZ, a pulse width of 0.5ms ~ 2.5ms and a 12 bit resolution (4096). PCA9685 uses a 12 bit register to control the PWM ratio. For 0.5ms, it is equivalent to the register value of 0.5 / 204096 = 102.
0.5ms-------0 degree
2.5ms -------- 180 degrees
And so on
The following is an example in the library

/*************************************************** 
  This is an example for our Adafruit 16-channel PWM & Servo driver
  Servo test - this will drive 8 servos, one after the other on the
  first 8 pins of the PCA9685

  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/815
  
  These drivers use I2C to communicate, 2 pins are required to  
  interface.

  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.  
  BSD license, all text above must be included in any redistribution
 ****************************************************/

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);

// Depending on your servo make, the pulse width min and max may vary, you 
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN  150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN  600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX  2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates

// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("8 channel Servo test!");

  pwm.begin();
  /*
   * In theory the internal oscillator (clock) is 25MHz but it really isn't
   * that precise. You can 'calibrate' this by tweaking this number until
   * you get the PWM update frequency you're expecting!
   * The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
   * is used for calculating things like writeMicroseconds()
   * Analog servos run at ~50 Hz updates, It is importaint to use an
   * oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
   * 1) Attach the oscilloscope to one of the PWM signal pins and ground on
   *    the I2C PCA9685 chip you are setting the value for.
   * 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
   *    expected value (50Hz for most ESCs)
   * Setting the value here is specific to each individual I2C PCA9685 chip and
   * affects the calculations for the PWM update frequency. 
   * Failure to correctly set the int.osc value will cause unexpected PWM results
   */
  pwm.setOscillatorFrequency(27000000);
  pwm.setPWMFreq(SERVO_FREQ);  // Analog servos run at ~50 Hz updates

  delay(10);
}

// You can use this function if you'd like to set the pulse length in seconds
// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. It's not precise!
void setServoPulse(uint8_t n, double pulse) {
  double pulselength;
  
  pulselength = 1000000;   // 1,000,000 us per second
  pulselength /= SERVO_FREQ;   // Analog servos run at ~60 Hz updates
  Serial.print(pulselength); Serial.println(" us per period"); 
  pulselength /= 4096;  // 12 bits of resolution
  Serial.print(pulselength); Serial.println(" us per bit"); 
  pulse *= 1000000;  // convert input seconds to us
  pulse /= pulselength;
  Serial.println(pulse);
  pwm.setPWM(n, 0, pulse);
}

void loop() {
  // Drive each servo one at a time using setPWM()
  Serial.println(servonum);
  for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);
  for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
    pwm.setPWM(servonum, 0, pulselen);
  }

  delay(500);

  // Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
  // The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior. 
  for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);
  for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
    pwm.writeMicroseconds(servonum, microsec);
  }

  delay(500);

  servonum++;
  if (servonum > 5) servonum = 0; // Testing the first 8 servo channels
}


Download this program. After converting the corresponding steering gear angle to pulse width, you can use IIC to control multiple steering gears at the same time. Here I use five steering gears

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// Default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define SERVO_0  102 
#define SERVO_45  187 
#define SERVO_90  280 
#define SERVO_135  373 
#define SERVO_180  510 

// our servo # counter
uint8_t servonum = 0;
char comchar;

void setup() {
  Serial.begin(9600);
  Serial.println("8 channel Servo test!");

  pwm.begin();
  pwm.setPWMFreq(50);  // 50HZ update frequency, equivalent to 20ms cycle

  delay(10);
}

void loop() {
    while(Serial.available()>0){
    comchar = Serial.read();//Read the first byte of serial port
    switch(comchar)
    {
      case '0':
      pwm.setPWM(0, 0, SERVO_0);
      Serial.write(comchar);
      break;
      case '1':
      pwm.setPWM(0, 0, SERVO_45);
      Serial.write(comchar);
      break;
      case '2':
      pwm.setPWM(0, 0, SERVO_90);
      Serial.write(comchar);
      break;
      case '3':
      pwm.setPWM(0, 0, SERVO_135);
      Serial.write(comchar);
      break;       
      case '4':
      pwm.setPWM(0, 0, SERVO_180);
      Serial.write(comchar);
      break;
      default:
      Serial.write(comchar);
      break;                  
    }
  }
}

This realizes the control of multiple fixed angles of a steering gear through simple conversion. If you want to fine control each steering gear, you can construct a conversion function and then realize the fine control of each steering gear

SPI protocol

SPI(Serial Peripheral Interface, Serial peripheral interface), yes Areuino A built-in high-speed communication interface through which external devices with the same interface can be connected. SPI It is duplex communication, so it is often used in external devices with large amount of data transmission

Pin of SPI device

Pinexplain
MISO(Master in Slave out)Host data input, slave data output
MOSIMaster data output slave data input
SCK(Serial Clock)A clock signal for synchronous communication, which is generated by the host
SS (SLAVE select)Slave enable signal

There are also resident slaves in SPI bus. The master is responsible for outputting the clock signal and selecting the communication slave. The clock signal will be output through the SCK pin of the master and provided to the communication slave. The selection of the communication slave is determined by the SS pin of the slave. When the SS pin is low, the slave is selected
SPI class library member functions

  1. SPI.begin()
    Initialize SPI communication. After calling this function, SCK/MOSI/SS pin will be set to output mode, and SCK/MOSI pin will be pulled down and SS pin will be pulled up.

  2. SPI.end()
    Turn off SPI bus communication

  3. SPI.setBitOrder(order)
    Set the transfer order. Order: transmission sequence, LSBFIRST, low order first; MSBFIRST, high in front

  4. SPI.setClockDivider(divider)
    Set the communication clock, which is generated by the host and not configured by the slave. divider: obtained by dividing the system clock of SPI communication. SPI is optional_ CLOCK_ DIV2,SPI_ CLOCK_ Div4 (default configuration), up to 128 frequency division

  5. SPI.setDataMode(mode)
    Set the data mode. Mode: configurable mode with SPI options_ MODE0,SPI_MODE1,SPI_MODE2,SPI_MODE3

  6. SPI.transfer(val)
    When transmitting 1Byte data, SPI is full duplex communication, so if you send 1B data, you will also receive 1B data. val: byte data to be sent.
    Original link
    arduino's SPI library only provides examples of host communication

Experiment: SPI communication

Because there was no official explanation on how to realize SPI communication between ARDUINO, I searched on the Internet and finally found a clear one
Original link

master Host code
#include <SPI.h>

void setup (void)
{

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
}  // end of setup


void loop (void)
{

  char c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Hello, world!\n" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (1000);  // 1 seconds delay 
} 

slave Slave code

#include <SPI.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

void setup (void)
{
  Serial.begin (115200);   // debugging
  
  // turn on SPI in slave mode
  SPCR |= bit (SPE);

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // get ready for an interrupt 
  pos = 0;   // buffer empty
  process_it = false;

  // now turn on interrupts
  SPI.attachInterrupt();

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
  
  // add to buffer if room
  if (pos < (sizeof (buf) - 1))
    buf [pos++] = c;
    
  // example: newline means time to process buffer
  if (c == '\n')
    process_it = true;
      
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;  
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // end of flag set
    
}


Serial port can see the program effect

Software simulation SPI communication

Analog SPI communication can specify any digital pin on ARDUINO as analog SPI pin function and communicate with other SPI devices

Experiment: 74HC595 was used

When the ARDUINO pin is not enough, the 74HC595 can be used to expand the I/O port

#include <SPI.h>
#define STCP 8
#define DS 51
#define HSCP 50
void setup(){
  pinMode(STCP,OUTPUT);
  pinMode(HSCP,OUTPUT);
  pinMode(DS,OUTPUT);
}

void loop(){
  for (int i=0;i<256;i++){
    digitalWrite(STCP,LOW);
    shiftOut(DS,HSCP,LSBFIRST,i);
    digitalWrite(STCP,HIGH);
    delay(50);
  }
}

Tags: arduino

Posted on Mon, 25 Oct 2021 00:24:02 -0400 by cityguru