diff --git a/book/06:串口通信/01.md b/book/06:串口通信/01.md index b108c61..bb7f0eb 100644 --- a/book/06:串口通信/01.md +++ b/book/06:串口通信/01.md @@ -186,4 +186,366 @@ ![image-20240926170851906](./img/image-20240926170851906.png) -# 2. HAL库外设初始化过程 \ No newline at end of file +# 2. HAL库外设初始化过程 + +## 2.1. 串口外设句柄 + +stm32f1xx_hal_uart.h 的160行; + +```c +typedef struct __UART_HandleTypeDef +{ + USART_TypeDef *Instance; /*!< UART registers base address */ + + UART_InitTypeDef Init; /*!< UART communication parameters */ + + const uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */ + + uint16_t TxXferSize; /*!< UART Tx Transfer size */ + + __IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */ + + uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */ + + uint16_t RxXferSize; /*!< UART Rx Transfer size */ + + __IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */ + + __IO HAL_UART_RxTypeTypeDef ReceptionType; /*!< Type of ongoing reception */ + + __IO HAL_UART_RxEventTypeTypeDef RxEventType; /*!< Type of Rx Event */ + + DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */ + + DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */ + + HAL_LockTypeDef Lock; /*!< Locking object */ + + __IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management + and also related to Tx operations. + This parameter can be a value of @ref HAL_UART_StateTypeDef */ + + __IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations. + This parameter can be a value of @ref HAL_UART_StateTypeDef */ + + __IO uint32_t ErrorCode; /*!< UART Error code */ + +#if (USE_HAL_UART_REGISTER_CALLBACKS == 1) + void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */ + void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */ + void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */ + void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */ + void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */ + void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Complete Callback */ + void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */ + void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Receive Complete Callback */ + void (* WakeupCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Wakeup Callback */ + void (* RxEventCallback)(struct __UART_HandleTypeDef *huart, uint16_t Pos); /*!< UART Reception Event Callback */ + + void (* MspInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp Init callback */ + void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Msp DeInit callback */ +#endif /* USE_HAL_UART_REGISTER_CALLBACKS */ + +} UART_HandleTypeDef; +``` + +和定时器的代码组织方式是一样的,包含和串口的属性以及操作的函数指针。基本解释可以参考教科书269页。 + +## 2.2. 串口初始化数据类型(p270) + +stm32f1xx_hal_uart.h 的46行; + +```c +typedef struct +{ + uint32_t BaudRate; /*!< This member configures the UART communication baud rate. + The baud rate is computed using the following formula: + - IntegerDivider = ((PCLKx) / (16 * (huart->Init.BaudRate))) + - FractionalDivider = ((IntegerDivider - ((uint32_t) IntegerDivider)) * 16) + 0.5 */ + + uint32_t WordLength; /*!< Specifies the number of data bits transmitted or received in a frame. + This parameter can be a value of @ref UART_Word_Length */ + + uint32_t StopBits; /*!< Specifies the number of stop bits transmitted. + This parameter can be a value of @ref UART_Stop_Bits */ + + uint32_t Parity; /*!< Specifies the parity mode. + This parameter can be a value of @ref UART_Parity + @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). */ + + uint32_t Mode; /*!< Specifies whether the Receive or Transmit mode is enabled or disabled. + This parameter can be a value of @ref UART_Mode */ + + uint32_t HwFlowCtl; /*!< Specifies whether the hardware flow control mode is enabled or disabled. + This parameter can be a value of @ref UART_Hardware_Flow_Control */ + + uint32_t OverSampling; /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to fPCLK/8). + This parameter can be a value of @ref UART_Over_Sampling. This feature is only available + on STM32F100xx family, so OverSampling parameter should always be set to 16. */ +} UART_InitTypeDef; +``` + +StopBits + +![image-20240929154747976](./img/image-20240929154747976.png) + +Parity + +![image-20240929154807419](./img/image-20240929154807419.png) + +Mode + +![image-20240929154830676](./img/image-20240929154830676.png) + +HwFlowCtl + +![image-20240929154857111](./img/image-20240929154857111.png) + +注意:一般串口通信都使用三根线,不会用到硬件流控。 + +OverSampling + +![image-20240929154951411](./img/image-20240929154951411.png) + +## 2.3. 串口初始化过程 + +![image-20240929162341424](./img/image-20240929162341424.png) + +1. MX_USART2_UART_Init:该函数位于main.c,有框架自动生成;用于初始化串口句柄; +2. HAL_UART_Init:位于 stm32f1xx_hal_uart.c;这是硬件抽象的初始化代码,并不关心具体的硬件,其中调用了HAL_UART_MspInit,用于实现真正的串口初始化功能 ;该文件中有个 HAL_UART_MspInit 函数的 weak定义; +3. HAL_UART_MspInit:位于 stm32f1xx_hal_msp.c;这里才是真正关于串口的初始化; + +因此:stm32f1xx_hal_uart.c 中定义的是操作硬件的接口(类似Java的接口,所有的函数,常量等,编程一般也只需要和该文件打交道);stm32f1xx_hal_msp.c 是真正的实现,实现具体的硬件层面的操作。这样的设计就可以不改变接口的情况下,适配多个不同硬件。 + +# 3. 轮询方式的串口通信 + +## 3.1. 轮询方式的接口函数 + +### 3.1.1. 串口初始化函数(HAL_UART_Init) + +该函数用于串口的初始化。将串口外设句柄中的参数写人对应的寄存器,并调用MCU硬件初始化函数HAL_UART_MspInit()完成时钟、引脚和中断等系统级初始化操作。具体描述如表 9-9所示。 + +![image-20240929151719021](./img/image-20240929151719021.png) + +### 3.1.2. 串口轮询方式发送丽数(HAL UART_Transmit) + +该函数用于在轮询方式下发送指定数量的数据。具体描述如表9-10所示 + +![image-20240929151812868](./img/image-20240929151812868.png) + +### 3.1.3. 串口轮询方式接收函数(HAL UART_Receive) + +该函数用于在轮询方式下接收指定数量的数据。具体描述如表9-11所示。 + +![image-20240929151902186](./img/image-20240929151902186.png) + +## 3.2. 固定长度的数据收发 + +1. 开发板串口使用PA2和PA3: + +![image-20240929152437856](./img/image-20240929152437856.png) + + +点击任意一个端口: + +![image-20240929152348644](./img/image-20240929152348644.png) + +知道使用的是串口2 + +2. 设置串口2的参数: + +![image-20240929152813604](./img/image-20240929152813604.png) + +问题: + +1. Hardware Flow Control 是什么意思? +2. Over Sampling (过采样)是什么意思? + + + +3. 代码编写: + +```c + /* USER CODE BEGIN 2 */ + uint8_t uartBuf[5]; + /* USER CODE END 2 */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 3 */ + if (HAL_UART_Receive(&huart2, uartBuf, 5, 100) == HAL_OK) { + HAL_UART_Transmit(&huart2, uartBuf, 5, 100); + } + } + /* USER CODE END 3 */ +} +``` + +下载程序后,连接TYPE-C到计算机的USB(可能需要安装驱动),然后打开串口通信软件,设置波特率等参数与开发板一致;发送5个字符,会收到开发板的回应(必须是一次性发送5个字符,不能分开发送)。 + +# 4. 中断方式的串口通信 + +中断的效率更高。 + +## 4.1. 中断方式的接口函数 + +### 4.1.1. 串口中断方式发送函数(HAL_UART_Transmit IT) + +该函数用于在中断方式下发送指定数量的数据,具体描述如表9-12所示: + +![image-20240929163645757](./img/image-20240929163645757.png) + +### 4.1.2. 串口中断方式接收函数(HALUART_Receive_IT) + +该函数用于在中断方式下接收指定数量的数据,具体描述如表9-13 所示: + +![image-20240929163815598](./img/image-20240929163815598.png) + +### 4.1.3. 串口中断通用处理函数(HAL_UART_IRQHandler) + +该函数是所有串口中断发生后的通用处理程序。任何一个串口的相关中断(如发送中断或接收中断)发生后,都会通过中断向量表中的串口中断服务程序USARTx_IRQHandler()调用该函数。在函数内部会完成具体的数据收发,最后调用不同的回调函数来完成后续的中断处理任务。具体描述如表 9-14所示: + +![image-20240929163945133](./img/image-20240929163945133.png) + +### 4.1.4. 串口发送中断回调函数(HAL_UART_TxCpltCallback) + +该函数用于处理在中断方式下发送完指定数量数据后的后续任务。任何一个串口发送完指定数量数据后,都会调用发送中断回调函数。因此,在函数内部需要判断是哪一个串口产生的本次发送中断回调,然后再执行具体的中断处理任务。具体描述如表9-15所示: + +![image-20240929164054977](./img/image-20240929164054977.png) + +### 4.1.5. 串口接收中断回调函数(HAL_UART_RxCpltCallback) + +该函数用于处理在中断方式下接收完指定数量数据后的后续任务。任何一个串口接收完指定数量数据后,都会调用接收中断回调函数。因此,在函数内部需要判断是哪一个串口产生的本次接收中断回调,然后再执行具体的中断处理任务。具体描述如表9-16所示: + +![image-20240929164202400](./img/image-20240929164202400.png) + +### 4.1.6. 串口中断使能函数(__HAL_UART_ENABLE_IT) + +该函数用于使能对应的串口中断类型,采用带参数的宏实现(宏函数),具体描述如表 9-17 所示: + +![image-20240929164333113](./img/image-20240929164333113.png) + +### 4.1.7. 串口中断标志查询函数(__HAL_UART_GET_FLAG) + +该函数用于查询对应的串口中断标志是否置位,采用带参数的宏实现(宏函数),具体描述如表 9-18 所示: + +![image-20240929164437770](./img/image-20240929164437770.png) + +### 4.1.8. 空闲中断标志清除函数( __HAL_UART_CLEAR_IDLEFLAG) + +该函数用于清除串口的空闲中断标志,采用带参数的宏实现(宏函数),具体描述如表 9-19 所示: + +![image-20240929164531359](./img/image-20240929164531359.png) + +## 4.2. 任务:使用中断方式实现简单的通信协议(p289) + +1. 与查询方式一样的设置串口,需要打开串口的全局中断: + +![image-20240929181629409](./img/image-20240929181629409.png) + +2. 设置PA6为输出,Label为LED1;PA7为输出,Label为LED2 +3. 完善代码: + +```c +/* USER CODE BEGIN 0 */ +#define LENGTH 4 // 接收帧的长度 +uint8_t RxBuffer[LENGTH]; // 接收缓冲 +volatile uint8_t RxFlag = 0; // 是否收到数据的标志 + +const char *MSG1 = "Please enter instruction."; +const char *MSG2 = "Head->0xAA Device->0x01 Operation->0x00/0x01 Tail->0x55."; +const char *ERRMSG = "Communication Error! Please try again!"; + +// 中断回调函数 +void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { + if (huart->Instance == USART2) { + RxFlag = 1; // 设置帧标志,主函数处理这个标志 + HAL_UART_Receive_IT(&huart2, RxBuffer, LENGTH); // 再次启动中断接收 + } +} +/* USER CODE END 0 */ +``` + +```c +int main(void) { + + /* USER CODE BEGIN 1 */ + + /* USER CODE END 1 */ + + /* MCU Configuration--------------------------------------------------------*/ + + /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ + HAL_Init(); + + /* USER CODE BEGIN Init */ + + /* USER CODE END Init */ + + /* Configure the system clock */ + SystemClock_Config(); + + /* USER CODE BEGIN SysInit */ + + /* USER CODE END SysInit */ + + /* Initialize all configured peripherals */ + MX_GPIO_Init(); + MX_USART2_UART_Init(); + /* USER CODE BEGIN 2 */ + HAL_UART_Transmit(&huart2, (uint8_t*) MSG1, strlen(MSG1), 100); // 发送提示 + HAL_UART_Transmit(&huart2, (uint8_t*) MSG2, strlen(MSG2), 100); // 发送提示 + + HAL_UART_Receive_IT(&huart2, RxBuffer, LENGTH); // 启动中断接收 + /* USER CODE END 2 */ + + /* Infinite loop */ + /* USER CODE BEGIN WHILE */ + while (1) { + /* USER CODE END WHILE */ + + /* USER CODE BEGIN 3 */ + if (RxFlag) { // 判断帧接收标志 + RxFlag = 0; // 清除帧接收标志 + if (RxBuffer[0] == 0xAA && RxBuffer[3] == 0x55) { // 判断头尾数据是否合法 + switch (RxBuffer[1]) { + case 1: // 灯1 + if (RxBuffer[2]) + HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, + GPIO_PIN_SET); + else + HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, + GPIO_PIN_RESET); + break; + case 2: // 灯2 + if (RxBuffer[2]) + HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, + GPIO_PIN_SET); + else + HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, + GPIO_PIN_RESET); + break; + } + } else { + // 非法格式,返回错误 + HAL_UART_Transmit(&huart2, (uint8_t*) ERRMSG, strlen(ERRMSG), + 100); + } + } + } + /* USER CODE END 3 */ +} +``` + +思考: + +1. 这个简单的格式解析是通信当中的语法和意义;但是还有些不完善,会存在什么缺陷? +2. 应该如何解决这些问题? + +# 5. DMA方式的串口通信 \ No newline at end of file diff --git a/book/06:串口通信/img/image-20240929151719021.png b/book/06:串口通信/img/image-20240929151719021.png new file mode 100644 index 0000000..09045ef Binary files /dev/null and b/book/06:串口通信/img/image-20240929151719021.png differ diff --git a/book/06:串口通信/img/image-20240929151812868.png b/book/06:串口通信/img/image-20240929151812868.png new file mode 100644 index 0000000..95d20d7 Binary files /dev/null and b/book/06:串口通信/img/image-20240929151812868.png differ diff --git a/book/06:串口通信/img/image-20240929151902186.png b/book/06:串口通信/img/image-20240929151902186.png new file mode 100644 index 0000000..836145d Binary files /dev/null and b/book/06:串口通信/img/image-20240929151902186.png differ diff --git a/book/06:串口通信/img/image-20240929152348644.png b/book/06:串口通信/img/image-20240929152348644.png new file mode 100644 index 0000000..8fb56dc Binary files /dev/null and b/book/06:串口通信/img/image-20240929152348644.png differ diff --git a/book/06:串口通信/img/image-20240929152437856.png b/book/06:串口通信/img/image-20240929152437856.png new file mode 100644 index 0000000..e76a981 Binary files /dev/null and b/book/06:串口通信/img/image-20240929152437856.png differ diff --git a/book/06:串口通信/img/image-20240929152813604.png b/book/06:串口通信/img/image-20240929152813604.png new file mode 100644 index 0000000..c4f0b71 Binary files /dev/null and b/book/06:串口通信/img/image-20240929152813604.png differ diff --git a/book/06:串口通信/img/image-20240929154747976.png b/book/06:串口通信/img/image-20240929154747976.png new file mode 100644 index 0000000..7497356 Binary files /dev/null and b/book/06:串口通信/img/image-20240929154747976.png differ diff --git a/book/06:串口通信/img/image-20240929154807419.png b/book/06:串口通信/img/image-20240929154807419.png new file mode 100644 index 0000000..0cdc989 Binary files /dev/null and b/book/06:串口通信/img/image-20240929154807419.png differ diff --git a/book/06:串口通信/img/image-20240929154830676.png b/book/06:串口通信/img/image-20240929154830676.png new file mode 100644 index 0000000..11861db Binary files /dev/null and b/book/06:串口通信/img/image-20240929154830676.png differ diff --git a/book/06:串口通信/img/image-20240929154857111.png b/book/06:串口通信/img/image-20240929154857111.png new file mode 100644 index 0000000..1df182e Binary files /dev/null and b/book/06:串口通信/img/image-20240929154857111.png differ diff --git a/book/06:串口通信/img/image-20240929154951411.png b/book/06:串口通信/img/image-20240929154951411.png new file mode 100644 index 0000000..6463e19 Binary files /dev/null and b/book/06:串口通信/img/image-20240929154951411.png differ diff --git a/book/06:串口通信/img/image-20240929162341424.png b/book/06:串口通信/img/image-20240929162341424.png new file mode 100644 index 0000000..07279ba Binary files /dev/null and b/book/06:串口通信/img/image-20240929162341424.png differ diff --git a/book/06:串口通信/img/image-20240929163603134.png b/book/06:串口通信/img/image-20240929163603134.png new file mode 100644 index 0000000..0f09002 Binary files /dev/null and b/book/06:串口通信/img/image-20240929163603134.png differ diff --git a/book/06:串口通信/img/image-20240929163645757.png b/book/06:串口通信/img/image-20240929163645757.png new file mode 100644 index 0000000..3df47c2 Binary files /dev/null and b/book/06:串口通信/img/image-20240929163645757.png differ diff --git a/book/06:串口通信/img/image-20240929163815598.png b/book/06:串口通信/img/image-20240929163815598.png new file mode 100644 index 0000000..48f5067 Binary files /dev/null and b/book/06:串口通信/img/image-20240929163815598.png differ diff --git a/book/06:串口通信/img/image-20240929163945133.png b/book/06:串口通信/img/image-20240929163945133.png new file mode 100644 index 0000000..c306014 Binary files /dev/null and b/book/06:串口通信/img/image-20240929163945133.png differ diff --git a/book/06:串口通信/img/image-20240929164054977.png b/book/06:串口通信/img/image-20240929164054977.png new file mode 100644 index 0000000..127a3dd Binary files /dev/null and b/book/06:串口通信/img/image-20240929164054977.png differ diff --git a/book/06:串口通信/img/image-20240929164202400.png b/book/06:串口通信/img/image-20240929164202400.png new file mode 100644 index 0000000..0bd8ba7 Binary files /dev/null and b/book/06:串口通信/img/image-20240929164202400.png differ diff --git a/book/06:串口通信/img/image-20240929164333113.png b/book/06:串口通信/img/image-20240929164333113.png new file mode 100644 index 0000000..3d8de05 Binary files /dev/null and b/book/06:串口通信/img/image-20240929164333113.png differ diff --git a/book/06:串口通信/img/image-20240929164437770.png b/book/06:串口通信/img/image-20240929164437770.png new file mode 100644 index 0000000..a86f069 Binary files /dev/null and b/book/06:串口通信/img/image-20240929164437770.png differ diff --git a/book/06:串口通信/img/image-20240929164531359.png b/book/06:串口通信/img/image-20240929164531359.png new file mode 100644 index 0000000..e750fc5 Binary files /dev/null and b/book/06:串口通信/img/image-20240929164531359.png differ diff --git a/book/06:串口通信/img/image-20240929181629409.png b/book/06:串口通信/img/image-20240929181629409.png new file mode 100644 index 0000000..fd1752c Binary files /dev/null and b/book/06:串口通信/img/image-20240929181629409.png differ