STM32f407 serial communication

Physical layer: stipulates the characteristics of mechanical and electronic functional parts in the communication system, and ensures the transmission of original data in physical media. In fact, it is the hardware part.
Protocol layer: The protocol layer mainly stipulates the communication logic, and unifies the data packaging and unpacking standards of the sending and receiving parties. It’s actually the software part.
Simply put, the physical layer stipulates whether we communicate with our mouth or body, and the protocol layer stipulates whether we communicate in Chinese or English.

RS232 structure diagram

1. RS232 standard serial port is mainly used for direct communication of industrial equipment
2. Level conversion chips generally include MAX3232, SP3232

USB to serial port structure diagram

1. The USB to serial port is mainly used for communication between the device and the computer
2. Level conversion chips generally include CH340, PL2303, CP2102, FT232
3. When using, the driver of the level conversion chip needs to be installed on the computer side

serial to serial

1. The original serial port communication is mainly the communication between the controller and the serial port device or sensor. It does not need to go through the level conversion chip to convert the level, and directly communicate with TTL level
2. GPS module, GSM module, serial port to WIFI module, HC05 Bluetooth module

The baud rate is how many bits are transmitted per second

Serial port functional block diagram

send

USART_CR1: UE (USART enable), TE (transmit enable), RE (receive enable)

USART_SR: TXE, Transmit data register empty

USARTCR1: TXEIE//Send data flag bit
US ART_SR: TC, Transmission complete

USARTCR1: TCIE/transmission complete flag bit

Baud rate – how many bits/s of data to send per second
USART_BRR: baud rate register, sub-integer and fractional parts USART_CR1: OVER8

USARTDIV: unsigned fixed-point number
FPCLK: The clock of the serial port, note to distinguish two buses of APB2 and APB1

OVER8: oversampling mode

Serial port clock initialization structure

typedef struct
{

  uint16_t USART_Clock; /*!< Specifies whether the USART clock is enabled or disabled.
                               This parameter can be a value of @ref USART_Clock synchronous clock CR2_CLKEN*/

  uint16_t USART_CPOL; /*!< Specifies the steady state of the serial clock.
                               This parameter can be a value of @ref USART_Clock_Polarity Polarity CR2_CPOL*/

  uint16_t USART_CPHA; /*!< Specifies the clock transition on which the bit capture is made.
                               This parameter can be a value of @ref USART_Clock_Phase CR2_CPHA*/

  uint16_t USART_LastBit; /*!< Specifies whether the clock pulse corresponding to the last transmitted
                               data bit (MSB) has to be output on the SCLK pin in synchronous mode.
                               This parameter can be a value of @ref USART_Last_Bit The clock pulse of the last bit CR2_LBC*/
} USART_ClockInitTypeDef;
//serial port initialization structure
typedef struct
{
  uint32_t USART_BaudRate; /*!<this member configures the USART communication baud rate.
                                           The baud rate is calculated using the following formula:
                                            - IntegerDivider = ((PCLKx) / (8 * (OVR8 + 1) * (USART_InitStruct->USART_BaudRate)))
                                            - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 8 * (OVR8 + 1)) + 0.5
                                           Where OVR8 is the "oversampling by 8 mode" configuration bit in the CR1 register. word length*/

  uint16_t USART_WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref USART_Word_Length */

  uint16_t USART_StopBits; /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref USART_Stop_Bits stop bits */

  uint16_t USART_Parity; /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref USART_Parity check control
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */
 
  uint16_t USART_Mode; /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref USART_Mode mode selection */

  uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
                                           or disabled.
                                           This parameter can be a value of @ref USART_Hardware_Flow_Control hardware flow control */
} USART_InitTypeDef;

Commonly used library functions

1-Configure GPIO as a specific second function
void GPIO_Pin AF Config
(GPIO_TypeDef*GPIO x, uint 16_t GPIO_Pin Source, uint 8_t GPIO_AF)
2-Interrupt configuration function
void US ART_IT Config
(US ART_TypeDef*US ART x, uint 16_tUS ART_IT, Functional State New State)
3- Serial port enable function
void US ART_Cmd (US ART_TypeDef*US ART x, Functional State New State)
4- Data sending function
void USART_SendData
(USART_TypeDef* USARTx, uint16_t Data)
5- Data receiving function
uint16_t USART_ReceiveData (USART_TypeDef* USARTx)
6-Interrupt status bit acquisition function ITStatus USART_GetITStatus
(USART_TypeDef* USARTx, uint16_t USART_IT)

1- GPIO needed to initialize the serial port, GPIO_InitTypeDef, GPIO_PinAFConfig ();
2- Initialize the serial port, USART_InitTypeDef
3- Interrupt configuration
4-Enable the serial port
5- Write send and receive functions
6- Write interrupt service function

 /**
  * @brief Configure nested vector interrupt controller NVIC serial port interrupt service function
  * @param None
  * @retval None
  */
static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* Nested Vectored Interrupt Controller Group Selection */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* Configure UART as interrupt source */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_UART_IRQ;
  /* steal priority is 1 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* subpriority is 1 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* enable interrupt */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* Initialize and configure NVIC */
  NVIC_Init( &NVIC_InitStructure);
}


 /**
  * @brief DEBUG_UART GPIO configuration, working mode configuration. 115200 8-N-1, interrupt receive mode
  * @param None
  * @retval None
  */
void Debug_UART_Config(void)
{
//Define the initialization structure
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
//The first step is to initialize GPIO
  RCC_AHB1PeriphClockCmd(DEBUG_UART_RX_GPIO_CLK|DEBUG_UART_TX_GPIO_CLK,ENABLE);

  
  /* GPIO initialization */
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  
  /* Configure the Tx pin as an alternate function */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_UART_TX_PIN;
  GPIO_Init(DEBUG_UART_TX_GPIO_PORT, & GPIO_InitStructure);

  /* Configure the Rx pin as an alternate function */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = DEBUG_UART_RX_PIN;
  GPIO_Init(DEBUG_UART_RX_GPIO_PORT, & GPIO_InitStructure);
  
//What is the second function of GPIO multiplexing?
 /* Connect PXx to USARTx_Tx*/
  GPIO_PinAFConfig(DEBUG_UART_RX_GPIO_PORT, DEBUG_UART_RX_SOURCE, DEBUG_UART_RX_AF);

  /* Connect PXx to USARTx__Rx*/
  GPIO_PinAFConfig(DEBUG_UART_TX_GPIO_PORT, DEBUG_UART_TX_SOURCE, DEBUG_UART_TX_AF);
  
//The second step configures the configuration serial port initialization structure
  /* Configuration string DEBUG_UART mode */
\t
/* Enable USART clock */
  RCC_APB1PeriphClockCmd(DEBUG_UART_CLK, ENABLE);
  /* Baud rate setting: DEBUG_UART_BAUDRATE */
  USART_InitStructure. USART_BaudRate = DEBUG_UART_BAUDRATE;
  /* word length (data bits + check digit): 8 */
  USART_InitStructure. USART_WordLength = USART_WordLength_8b;
  /* Stop bit: 1 stop bit */
  USART_InitStructure. USART_StopBits = USART_StopBits_1;
  /* Parity selection: no parity */
  USART_InitStructure. USART_Parity = USART_Parity_No;
  /* Hardware flow control: don't use hardware flow */
  USART_InitStructure. USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  /* USART mode control: enable both receive and transmit */
  USART_InitStructure. USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  /* Complete USART initialization configuration */
  USART_Init(DEBUG_UART, &USART_InitStructure);
\t
//The third step is to configure the receiving interrupt of the serial port
  /* Nested vectored interrupt controller NVIC configuration */
NVIC_Configuration();
  
/* Enable serial port receive interrupt */
USART_ITConfig(DEBUG_UART, USART_IT_RXNE, ENABLE);
\t
  /* The fourth step is to enable the serial port */
  USART_Cmd(DEBUG_UART, ENABLE);
}
//The fifth step send and receive function
/****************** Send a character ***********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* Send a byte of data to the USART */
USART_SendData(pUSARTx,ch);
\t\t
/* Wait for the send data register to be empty */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/****************** Send String ***********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
  do
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* Wait for the send to complete */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

The GPIO clock of serial port 1 is on AHB1, the serial port clock is on APB2, the clock of AHB1 is 168M, and the clock of APB2 is 84M.

DMA receive interrupt:

 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // Receive data register is not empty interrupt
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//Idle line detection interrupt

The preemption priority of serial port 1 is 10 and the sub-priority is 0. DMA is not used.

The serial port 2GPIO clock is at AHB1, the serial port clock is at APB1, the AHB1 clock is 168M, and the APB1 clock is 42M.

The preemption priority of serial port 2 is 10 and the sub-priority is 2. Serial port 2 has no DMA.

The serial port 3GPIO clock is at AHB1, the serial port clock is at APB1, the AHB1 clock is 168M, and the APB1 clock is 42M.

The preemption priority of serial port 3 is 10 and the sub-priority is 0. DMA is not used.

The serial port 6GPIO clock is on AHB1, the serial port clock is on APB1, the AHB1 clock is 168M, and the APB2 clock is 84M.

The preemption priority of serial port 6 is 10 and the sub-priority is 2. DMA is not used.

USART_GetFlagStatus(USART1, USART_FLAG_RXNE);

3) RXNE (read data register is not empty), when this bit is set to 1, it means that data has been received and can be read out. At this time, what we have to do is to read USART_DR as soon as possible. By reading USART_DR, this bit can be cleared, or write 0 to this bit to clear it directly.
//Judge whether the non-empty interrupt flag of the receiving data register is 0, if it is 1, it proves that there is data received and can be read.

USART_GetFlagStatus(USART1, USART_FLAG_TC);

TC (transmission complete), when this bit is set, it means that the data in USART_DR has been sent. like

If the interrupt of this bit is set, an interrupt will be generated. There are two ways to clear this bit: 1) Read USART_SR, write

USART_DR. 2) Write 0 to this bit directly.


USART_GetFlagStatus(USART1, USART_FLAG_IDLE)

IDLE idle line detection, when the position is 1, it proves that the line is idle and needs to be cleared, read USART_SR, read

USART_DR
//When the receiving data register is 0, judge whether the serial port 1 is an idle line, when the serial port 1 is idle

xSemaphoreGiveFromISR(BinarySemaphore_Uart1, & amp;xHigherPriorityTaskWoken);//Interrupt level semaphore release function
USART1->SR & amp; 0X40//Serial port 1 is sending, wait for the 6th bit of SR register to be 0 and exit the loop
uint16_t Uart_Crc_Make(uint16_t size, uint8_t *buff)
{
uint16_t car, i;
uint16_t crc[2];
uint16_t CrcCodeTmp;

crc[0] = 0xff;
crc[1] = 0xff;
for ( i = 0; i < size; i ++ ) {
car = buff[i];
car ^= crc[0];
\t\t
if(car>256) //It is found that there is a small probability of overflow here, increase and return directly
return 0;
\t\t
crc[0] = crc[1] ^ crc_tab. TABLE2[car];
crc[1] = crc_tab. TABLE1[car];
}
/*
CrcCodeTmp=crc[0];
crc[0]=crc[1];
crc[1]=CrcCodeTmp;
return (*(uint16_t*)( &crc[0]));
*/

CrcCodeTmp = (crc[0]<<8) + crc[1];
return CrcCodeTmp;

}

/**************************************************** ***********/
 uint16_t Uart_crc_check( uint16_t data_len, uint8_t *data )
{
uint16_t Crc_Code;
 uint16_t Crc_LowCode, Crc_HighCode;
Crc_Code = Uart_Crc_Make( data_len, data); // Calculate the CRC check code for the received data
 Crc_HighCode = data[ data_len ];
 Crc_LowCode = data[ data_len + 1 ];
 if( Crc_Code == ( Crc_HighCode * 0x100 + Crc_LowCode ) ) //check code is correct
return 1;
 else
 return 0;
}


/****************************************
us delay function
Accurate measurement of 168m main frequency time_us=1 delay 1us
*****************************************/
void Uart_Delay_nus(uint16_t time_us)
{
uint16_t k1,k2;
for(k1=time_us;k1>0;k1--)
{
for(k2=33;k2>0;k2--)
{
}
}
}

Question: How are tasks, semaphores, and interrupts related?

In STM32f407, tasks, queues, semaphores and interrupts are all important concepts in multitasking operating systems.

A task is a program that runs independently and can be regarded as an independent thread. In STM32f407, tasks can be created and managed through the FreeRTOS operating system. Each task has its own task stack and task control block, and operations such as task creation, deletion, and switching can be realized through the API functions provided by FreeRTOS.

A queue is a data structure used to pass data between different tasks. In STM32f407, queues can be created and managed by the FreeRTOS operating system. Data can be passed between different tasks through the send and receive operations of the queue.

A semaphore is a synchronization mechanism used to coordinate execution among different tasks. In STM32f407, semaphores can be created and managed by the FreeRTOS operating system. The semaphore can be used to control the access resources of tasks, ensure the synchronization and mutual exclusion of tasks, and so on.

An interrupt is a hardware mechanism for responding to external events. In STM32f407, interrupts can be controlled by NVIC (Nested Vectored Interrupt Controller). Interrupts can be used to respond to external events, turning them into opportunities for task execution.

In STM32f407, tasks, queues, semaphores and interrupts are closely related through the FreeRTOS operating system and work together to implement complex multi-tasking applications. Data is passed between tasks through queues and semaphores, and interrupts can respond to external events and activate corresponding task execution.

How do interrupts respond to external events and activate corresponding task execution?

In STM32f407, interrupts can respond to external events and activate corresponding task execution. The specific implementation process is as follows:

  1. Assign an interrupt vector number to an external event and associate it with an interrupt handler. An interrupt handler is a special function that will run automatically when the corresponding interrupt occurs.

  2. Configure the NVIC (Nested Vectored Interrupt Controller) so that it can respond to the corresponding interrupt vector number. Through the NVIC, interrupts can be turned on and off, priority control, etc.

  3. When the corresponding external event occurs, the interrupt vector number will be sent to NVIC, and NVIC will check the interrupt priority and decide whether to trigger the interrupt according to the priority.

  4. If an interrupt is triggered, NVIC will jump to the corresponding interrupt handler and execute the code in it. The interrupt processing function will immediately save the interrupt scene, including the program counter, status register, etc., and execute the set processing code.

  5. The code in the interrupt handler usually includes performing the necessary operations and activating the associated tasks. For example, when an interrupt to read an external sensor occurs, the interrupt handler can read the sensor data and send it to the task queue so that other tasks can use the data for subsequent processing.

  6. If there are multiple tasks waiting for the same data at the same time, the task scheduler will switch to the corresponding task according to the priority, and use the queue receiving operation to obtain the data for processing. If the corresponding data is not available, the task will block until the data is available.

In this way, interrupts can respond to external events and activate related tasks for corresponding processing, thereby realizing the complex functions of multi-task applications.

How does the interrupt handler send data to the task queue?

Interrupt handlers can use task notifications or queues to send data to task queues.

Method 1: Task notification

Task notification is a lightweight synchronization mechanism that communicates through event notifications. The interrupt handler can use the xTaskNotifyFromISR() function to send a notification to the task, and pass the data as the parameter of the notification. Tasks can use the xTaskNotifyWait() function to wait for notifications and receive data.

Method 2: queue

A queue is a FIFO (first in first out) data structure that can pass data from one task to another. The interrupt handler can use the xQueueSendToBackFromISR() function to send data to the queue, and the task can use the xQueueReceive() function to receive data from the queue. It should be noted that when sending data to the queue in the interrupt processing function, the xQueueSendToBackFromISR() function must be used, otherwise it may cause data loss or deadlock.

Regardless of which method is used, the interrupt handler must use the FromISR suffix function to send data, and use the portYIELD_FROM_ISR() function in the interrupt service routine to manually switch tasks.

RSM485ECHT

RSM485ECHT is a RS485 converter for converting RS485 signal to TTL or CMOS signal. When using RSM485ECHT, it is necessary to select the corresponding connection method according to the specific application scenario.

If you need to convert the RS485 signal to TTL or CMOS signal, you can plug the RSM485ECHT into the RS485 bus, and you need to pay attention to the connection of the A and B ports when wiring.

If you need to use RSM485ECHT for data acquisition or communication, you need to connect RSM485ECHT through the serial port, and use the corresponding driver for initialization to ensure normal data communication.

For converter equipment such as RSM485ECHT, in general, no special initialization operation is required. After connecting the serial port, you only need to correctly set the parameters of the serial port (such as baud rate, data bit, parity bit, stop bit, etc.), and encode and decode the data according to the protocol requirements to realize data transmission.

If you need to make special settings (such as modifying the address of the device, communication mode, etc.), generally you need to set it through the corresponding configuration tool or command.

SN74LVC4245A

SN74LVC4245A is a bidirectional bus converter, which can realize bidirectional conversion between TTL/CMOS signal and 3.3V or 5V CMOS signal. The chip has the advantages of low power consumption, high speed, and high anti-interference, and is one of the commonly used chips in electronic product design.

SN74LVC4245A is easy to operate, only need to input TTL/CMOS signal to the DIR control terminal, then the bidirectional conversion of the signal can be realized. When DIR is high level, data is transmitted from terminal A to terminal B; when DIR is low level, data is transmitted from terminal B to terminal A. In addition, SN74LVC4245A also has an enable terminal (OE). When the enable terminal is at a low level, the output of the output port is in a high-impedance state.

The following points need to be paid attention to when using SN74LVC4245A:

  1. The input signal must match the chip power supply voltage, VCC is 3.3V or 5V.

  2. Pay attention to the amplitude of the input signal. If the amplitude of the input signal exceeds the power supply voltage of the chip, the chip may be damaged.

  3. The DIR control terminal, the enable terminal OE and the output port all need to be connected with appropriate pull-up or pull-down resistors to ensure signal stability.

In summary, the SN74LVC4245A is an easy-to-use and powerful bus converter chip that can play an excellent role in various digital circuit applications. When using, you need to pay attention to factors such as signal level, amplitude, connection mode of control terminal and output port.