Serial communication SPI, UART, I2C

Serial communication

1.SPI

SPI (Serial Peripheral Interface) is a synchronous serial data transmission standard proposed by Motorola Company. It is mainly used in EEPROM, FLASH, real-time clock, AD converter, digital signal processor and digital signal decoder.

First, let's talk about the concept of synchronization,

In the above figure, the Master is on the left and the Slave is on the right. SPI interface is often called 4-wire serial bus, which outputs in a Master-Slave mode. As shown in the figure above, the Master and Slave computers are connected by four data lines.

Synchronization refers to the communication mode in which the sender sends data and the receiver sends a response before sending the next data packet.

1. Introduction of four data lines:

(1) . SCLK is a serial clock used for synchronous data transmission and output by the host;

(2) . MOSI is the host output slave input data line, which usually preferentially transmits MSB;

(3) . MISO is the host input and slave output data line, usually preferentially transmitting LSB;

(4) . SS is the chip selection line. The low level is valid and is output by the host. In short, the slave is selected by selecting the chip selection line.

2. Data transmission:

As shown in the figure above, the master sends 1-bit data to the slave through the MOSI line, and the slave sends 1-bit data to the master through MISO (realized through the shift register). The master and slave form a cycle. When all the contents in the register are removed, it is equivalent to completing the exchange of contents between the two shift registers.

3. Clock polarity and clock phase

Clock polarity (CPOL or UCCKPL), clock phase (CPHA or UCCKPH).

Clock polarity: the polarity of the clock when it is idle.

Clock phase: set the clock edge of reading data and sending data (rising and falling edges are also used at the same time).

4. Advantages and disadvantages:

Excellent:

(1) it supports full duplex (i.e. master and slave data can be transmitted simultaneously without mutual interference), and there is no speed limit defined. The general implementation can usually reach or even exceed 10 Mbps.

(2) the operation is relatively simple

(3) high data transmission efficiency

Missing:

(1) it needs to occupy more I/O port lines of the host, and each slave needs one.

(2). Only a single host is supported

5. Code explanation SPI:

host:

SPI main mode:
When the register UxBUF writes bytes, SPI main mode byte transfer begins. USART uses a baud rate generator to generate
It is a SCK serial clock and transmits the bytes provided by the send register to the output pin MOSI. At the same time, receive and send
The memory obtains the received bytes from the input pin MISO.

#include <ioCC2540.h>
#include "hal_cc8051.h"
 
#define LED1          P1_0
unsigned char temp = 0;  // Data transceiver cache
 
void SPI_Master_Init()
{
    CLKCONCMD = 0x80;
    while (CLKCONSTA != 0x80);      // The system clock is configured as 32MHz
    
    // SPI host mode configuration
    PERCFG |= 0x02;               // Spare position 2 for I/O using USART1
                                  // P1_4: SSN, P1_5: SCK, P1_6: MOSI, P1_7: MISO 
    P1SEL |= 0xE0;                // Configure P1_5,P1_6,P1_7 is peripheral function   
    P1SEL &= ~0x10;               // Configure P1_4 is universal I/O port (SSN)
    P1DIR |= 0x10;                // Configure SSN pin as output pin
    U1BAUD = 0x00; U1GCR |= 0x11; // Configure baud rate 4MHz
    U1CSR &= ~0xA0;               // Configured as SPI mode and SPI host
    U1GCR &= ~0xC0;               //When idle, SCLK is at low level, rising edge data acceptance and falling edge data transmission
    U1GCR |= 0x20;                // MSB (high byte) is transmitted first
}
 
void SPI_Master_Receive()
{
    P1_4 = 0;              // On the falling edge of SSN, SPI slave is active and starts sending and receiving data
    U1DBUF = 0x33;         // Send data to data cache register
    while(!(U1CSR&0x02));  // Wait for data transmission to complete, i.e. send completion flag position 1
    U1CSR &= 0xFD;         // Clear the transmission completion flag bit, that is, the transmission completion flag position 0
    temp = U1DBUF;         // Receive data from data cache register
    P1_4 = 1;              // On the rising edge of SSN, SPI slave is inactive and does not receive data
}
 
 
void P1_Init()
{
    P1DIR |= 0x01;
    LED1 = 0;
}
 
void main()
{
    SPI_Master_Init();
    P1_Init();
    
    for(;;) 
    {
      SPI_Master_Receive();
      
      if( temp == 0x11 )
      {
        LED1 = 1;
      }
      else
        LED1 = 0;
      halMcuWaitMs(300);
    }
    
}

SPI slave mode: (rising edge or falling edge trigger programmable control)
On the falling edge of SSN, SPI is active from mode, receives data on MOSI input and outputs data on MOSI output.
On the rising edge of SSN, SPI slave mode is inactive and does not receive data.

#include <ioCC2530.h>
 
#define LED2          P1_1
 
unsigned char temp=0;   // Data acceptance cache
 
void SPI_Slave_Init()
{
    CLKCONCMD = 0x80; while(CLKCONSTA != 0x80);  // The system clock is configured as 32MHz     
    
    // SPI slave mode configuration
    PERCFG |= 0x02;                // Spare position 2 for I/O using USART1
                                   // P1_4: SSN,P1_5: SCK,P1_6: MOSI,P1_7: MISO
    P1SEL |= 0xF0;                 // Configure P1_4,P1_5,P1_6,P1_7 is peripheral function
    U1BAUD = 0x00; U1GCR |= 0x11;  // Configure baud rate 4MHz
    U1CSR &= ~0x80; U1CSR |= 0x20; // Configured as SPI mode and SPI slave
    U1GCR &= ~0xC0;                //When idle, SCLK is at low level, rising edge data acceptance and falling edge data transmission
    U1GCR |= 0x20;                 // MSB (high byte) is transmitted first
}
 
void SPI_Slave_Receive()
{
    while (!(U1CSR&0x04)); // Wait for the completion of data acceptance (i.e. the acceptance completion flag is set to 1)
    U1CSR &= 0xFB;         // Clear the acceptance completion flag (i.e. the acceptance completion flag is set to 0)
    temp = U1DBUF;         // Read data from the data cache register and assign it to temp
}
 
void P1_Init()
{
    P1DIR |= 0x02;
    LED2 = 0;
}
 
void main()
{
    P1_Init();
    SPI_Slave_Init();
    
    for(;;) 
    {
      U1DBUF = 0x11;
      SPI_Slave_Receive();
      if( temp == 0x33 )
      {
        LED2 = 1;
      }
      else
        LED2 = 0;
    }
}

2.I2C

The I2C includes a clock line (SCL) and a data line (SDA). These two lines are of open drain or open collector structure. When in use, a pull-up resistor needs to be added. Multiple devices can be mounted (as shown in the figure below). Each device has its own address. The host selects different devices by selecting different addresses.

(since the blogger has not fully mastered the knowledge of analog and digital electricity, the open drain and open collector are not introduced here. In a word, the open drain output can only output low or close the output, so the open drain output must always be equipped with a pull-up resistor.)

1. General operation:

The host sends data to the slave

1. Send the START condition START and the Slave address (after the 8 bits of the address are transmitted, the Slave device that has successfully configured the address must send "ACK". Otherwise, after a certain period of time, the Master will be regarded as timeout, give up data transmission and send "Stop".);

2. Send data (when writing data, every time the Master sends 8 data bits, if the Slave device still has space to accept the next byte, it should answer "ACK". If the Slave device has no space to accept more bytes, it should answer "NACK". When the Master receives "NACK" Or if no data is received after a certain period of time, it will be regarded as timeout. At this time, the Master will give up data transmission and send "Stop".);

3. Sending STOP condition STOP ends.

The host reads data from the slave

1. Send the START condition START and the Slave address (after the 8 bits of the address are transmitted, the Slave device that has successfully configured the address must send "ACK". Otherwise, after a certain time, the Master will be deemed to have timed out and will give up the data transmission and send "Stop".);

2. Send the address to be read (when reading data, the Slave device sends 8 data bits every time. If the Master wants to continue reading the next byte, the Master should answer "ACK" to prompt the Slave to prepare for the next data. If the Master does not want to read more bytes, the Master should answer "NACK" to prompt the Slave device to prepare to receive the Stop signal.);

3. Read data;

4. Sending STOP condition STOP ends.

2. Start and end conditions:

When SCL remains at high level, SDA changes from high level to low level, that is, START.

When SCL remains at low level, SDA changes from low level to high level, which is STOP.

When reading data, you can START reading data repeatedly without sending STOP after sending the START condition START and slave address. Data is transmitted to MSB first. The receiver keeps the SDA low in the 9th clock cycle after each byte to confirm that the data reception is successful; Keeping the SDA high in the 9th clock cycle indicates that there is a data transmission error, or the host no longer wants to receive data.

3. Advantages and disadvantages:

Advantages:
(1) only two signal lines are used;
(2) support multiple hosts and multiple slaves (theoretically, the maximum number of master devices is unlimited, and the maximum number of slaves is 127);
(3) there is a response mechanism.
Disadvantages:
(1) the speed is slower than SPI.

4. Code explanation I2C:

The first, of course, is the definition header file, where SDA_IN and SDA_OUT is to set the input and output modes respectively.

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"

//IO input / output direction setting, operate CRL register
#define SDA_IN()  {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

//Set the pins of IIC data line and clock line
#define IIC_SCL    PBout(6) 		//SCL
#define IIC_SDA    PBout(7) 		//SDA	 
#define READ_SDA   PBin(7) 	 		//ÊäÈëSDA 

//IIC operation function
void IIC_Init(void);                //Initialize IO port of IIC 
void IIC_Start(void);				//Send IIC start signal
void IIC_Stop(void);	  			//Send IIC stop signal
void IIC_Send_Byte(u8 txd);	//Send a byte of data		
u8 IIC_Read_Byte(unsigned char ack);//IIC reads a byte
u8 IIC_Wait_Ack(void); 	//IIC waiting for response signal			
void IIC_Ack(void);					//IIC generates response signal
void IIC_NAck(void);			//IIC generates non response signal 
#endif

The transmission and reading of I2C data are as follows:

#include "myiic.h"
#include "delay.h"
//IIC initialization
void IIC_Init(void)
{					     
 	RCC->APB2ENR|=1<<3;		//Enable peripheral IO
	GPIOB->CRL&=0X00FFFFFF;	//PB6.7 clearing
	GPIOB->CRL|=0X33000000; //PB6.7 push pull output
	GPIOB->ODR|=3<<6;     	//PB6.7 output high
}
//Generate I2C start signal
//The conditions for I2C start signal generation are: SCL is high level and SDA becomes low level
void IIC_Start(void)
{
	SDA_OUT();     	//Set SDA to output mode
	IIC_SDA=1;	  	//Set the initial state to high level  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;		//Start signal, SDA from high to low
	delay_us(4);
	IIC_SCL=0; 		//Clamp the I2C bus and prepare to send or receive data
}	 

//Generate I2C stop signal
//The conditions for generating stop signal are: SCL is at high level and SDA changes from low to high
void IIC_Stop(void)
{
	SDA_OUT();//SDA set to output
	IIC_SCL=0;
	IIC_SDA=0;//Start with low level
 	delay_us(4);
	IIC_SCL=1; //SCL goes high
	IIC_SDA=1;//SDA changes from low level to high level to generate stop signal
	delay_us(4);							   	
}

//After the I2C master device transmits a data, the slave device generates a response signal, and the master device waits for the response signal
//Generation condition: when SCL is high level, SDA clock remains low level.
//Return value: 1. Failed to receive the response; 0, the response is received successfully
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();      			 //SDA set as input
	IIC_SDA=1;
    delay_us(1);	 //It was high at first
	IIC_SCL=1;
    delay_us(1);	 
	while(READ_SDA)		//Read the level state of the data line SDA. If the low level continues, IIC will not be generated_ Stop signal, return 0
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();//If the SDA signal line generates a high level for a certain period of time during the SCL high level, the response is considered to have failed
			return 1;
		}
	}
	IIC_SCL=0;//The response ends and the clock outputs 0
	return 0;  
} 

//Generate ACK response signal
//The generation condition is: during the period when SCL is high level, SDA always maintains low level
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//Generate non response signal
//The generation condition is: during the period when SCL is high level, SDA also appears high level  
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC sends a byte  
//The transmission conditions are: SCL is the data prepared during the low-level period, and SCL is the data held during the high-level period
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	  //SDA set to output
    IIC_SCL=0;//Pull down the clock to prepare the data
    for(t=0;t<8;t++)
    {              
      if((txd&0x80)>>7) //Start transmission from the highest bit of data
      		IIC_SDA=1;	//If 1, the data bit is 1
      	else IIC_SDA=0; //Not 1, data bit 0
        txd<<=1; 	  //Transfer one by one
		delay_us(2);   
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//Read a byte. When ack=1, send ack. When ack = 0, send nACK
//The reading condition is: when SCL is high level, read the level state of SDA
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//Set SDA as input
    for(i=0;i<8;i++ ) //Read 8 bits one by one
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1; //SCL is high
        receive<<=1; //Move data bits one by one
        if(READ_SDA)receive++;   //If SDA is high, the corresponding data is + 1, otherwise it is 0
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//No ACK response is generated
    else
        IIC_Ack(); //Generate ACK response
    return receive;
}

As described above, after initializing I2C, set start() and stop() functions to start and end signal transmission and reception (by adjusting the state of SDA and SCL), and then set ACK response function to realize the transmission of "stop" signal (that is, to end signal transmission and reception, it is also realized by adjusting SDA and SCL).

3.UART communication

UART is an asynchronous transmission interface. It does not need a clock line. It identifies data through start bit, stop bit and baud rate.

Asynchrony refers to the communication mode in which the sender sends data, does not wait for the receiver to send back the response, and then sends the next data packet.

As shown in the figure: RX (receive data) and TX (send data), the TX of one device needs to be connected with the RX of another device, and the RX of the same device needs to be connected with the TX of another device to complete data reception and transmission.

1. Data format:

(1) Start bit
The idle state of the data line is high. When you want to send data, pull it down by one clock cycle to represent the start bit.
(2) Data bit
When the check bit is used, the data bit can have 5 ~ 8 bits, generally 8 bits (to ensure the correctness of ASCII value). If the check bit is not used, the data bit can reach 9 bits.
(3) Check bit
Parity check ensures that the number of 1 in all bits including check bits and data bits is odd or even.
(4) Stop bit
In order to indicate the end of the packet, the transmitter needs to change the signal line from low level to high level and maintain at least 2 clock cycles.

2. Advantages and disadvantages:

advantage:

(1) . use only two signal lines

(2) . no clock signal is required

Disadvantages:

The transmission rate is relatively low.

3. Code explanation UART

The UART communication is explained below with a code of punctual atom 32:

u8 USART_RX_BUF[USART_REC_LEN];     //Receive buffer, maximum USART_REC_LEN bytes
//Receiving status
//bit15, 	 Reception completion flag
//bit14, 	 0x0d received
//bit13~0, 	 Number of valid bytes received
u16 USART_RX_STA=0;       //Receive status flag	  
  
void uart_init(u32 bound){
  //GPIO port settings
 	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//Enable USART1, GPIOA clock
  
	//USART1_TX   GPIOA9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//Multiplexed push-pull output
  GPIO_Init(GPIOA, &GPIO_InitStructure);//Initialize GPIOA9
   
  //USART1_RX 	   GPIOA10 initialization
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//Floating input
  GPIO_Init(GPIOA, &GPIO_InitStructure);//Initialize GPIOA.10  

  //Usart1 NVIC configuration
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//Preemption priority 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//Sub priority 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ channel enable
	NVIC_Init(&NVIC_InitStructure);	//Initializes the VIC register according to the specified parameters
  
   //USART initialization settings

	USART_InitStructure.USART_BaudRate = bound;//Serial baud rate
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//The word length is in 8-bit data format
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//A stop bit
	USART_InitStructure.USART_Parity = USART_Parity_No;//No parity bit
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//No hardware data flow control
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//Transceiver mode

  USART_Init(USART1, &USART_InitStructure); //Initialize serial port 1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//Open serial port to accept interrupt
  USART_Cmd(USART1, ENABLE);                    //Enable serial port 1 

}

According to the code rules of 32, initialize the pins and interrupts first, and then set the required baud rate (generally 9600 or 115200), the number of bytes received, and the set stop bit for data transmission and reception (because the clock difference at the receiving and transmitting end will not be too large within a byte, but the gap will become larger and larger when more data is transmitted and received. Therefore, after every 8-bit data is transmitted, the stop bit is used for clock synchronization, so the clock gap at the receiving and transmitting end is limited to an interval, which will not cause data reading disorder), whether there are parity bits, etc.

Next is the sending and receiving of data.

Data reception is as follows:

void USART1_IRQHandler(void)                	//Serial port 1 interrupt service program
	{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //Receive interrupt (the received data must end in 0x0D and 0x0a)
		{
		Res =USART_ReceiveData(USART1);	//Read received data
		
		if((USART_RX_STA&0x8000)==0)//Reception incomplete
			{
			if(USART_RX_STA&0x4000)//If 0x0d has been received
				{
				if(Res!=0x0a)USART_RX_STA=0;//If 0z0a is not received immediately after 0x0d is received, it is a reception error and restart
				else USART_RX_STA|=0x8000;	//Reception is complete 
				}
			else //I haven't received 0X0D yet
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//Error receiving data, restart receiving	  
					}		 
				}
			}   		 
     } 

Just like "if (usart_getitstatus (USART 1, usart_it_rxne)! = reset)" in the above code, this code is to identify whether the first and second bit data are 0x0d and 0x0a (i.e. whether the reception starts or not, because 0x0d is defined as the starting point and ends with 0x0a). After receiving 0x0d and 0x0a, 32 will store the data until "(usart_rx_sta & 0x8000) = = 0" To end reception.

Among them, USART_RX_STA is the variable to judge whether the signal is received or not, USART_ RX_ If the sta is 0000, the 16th bit is 0, the serial port data is not received completely, if it is 1, the serial port data is received completely (there is judgment in the interrupt), and 0x8000 = 1000 0000, so USART_ RX_ There are only two possibilities for sta (end of reception or end of non reception).

Data transmission is as follows:

int main(void)
 {		
 	u16 t;  
	u16 len;	
	u16 times=0;
	delay_init();	    	 //Delay function initialization	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //Set NVIC interrupt packet 2: 2-bit preemption priority and 2-bit response priority
	uart_init(115200);	 //The serial port is initialized to 115200
 	while(1)
	{
		if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;//Get the length of the data received this time
			printf("\r\n The message you sent is:\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART_SendData(USART1, USART_RX_BUF[t]);//Send data to serial port 1
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//Wait for sending to end
			}
			printf("\r\n\r\n");//Insert wrap
			USART_RX_STA=0;
		}else
		{
			times++;
			if(times%200==0)printf("Please enter data,End with enter\n");  
			delay_ms(10);   
		}
	}	 
 }

nfig(NVIC_PriorityGroup_2); // Set NVIC interrupt packet 2: 2-bit preemption priority and 2-bit response priority
uart_init(115200); // The serial port is initialized to 115200
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_ RX_ STA&0x3fff;// Get the length of the data received this time
Printf ("\ R \ nthe message you sent is: \ r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);// Send data to serial port 1
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);// Wait for sending to end
}
printf("\r\n\r\n");// Insert wrap
USART_RX_STA=0;
}else
{
times++;
if(times%200==0)printf("please enter data and end with enter");
delay_ms(10);
}
}
}

​		In the above code. “ USART_SendData(USART1, USART_RX_BUF[t])"It means to send data to serial port 1. The next code is to judge whether the segment data is sent. If it is sent, it will return set,Use if not completed while Keep sending status until data sending is completed.

Tags: stm32

Posted on Sat, 20 Nov 2021 09:53:24 -0500 by dookie