通过串口或蓝牙发送指令来控制led灯。


main.c中主要代码如下:
- #define UART1_REC_LEN 200
-
- uint16_t UART1_RX_STA=0;
- uint8_t buf=0;
- // 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
- uint8_t UART1_RX_Buffer[UART1_REC_LEN];
-
- void SystemClock_Config(void);
-
- // 接收中断
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
- if(huart->Instance != USART1) {
- return;
- }
- // 数据接收完成
- if((UART1_RX_STA & 0x8000) != 0) {
- HAL_UART_Receive_IT(&huart1, &buf, 1);
- return;
- }
- // 接收到回车之后判断后续的是不是换行,如果是换行,数据接收完成,但是还要开启一下中断
- if(UART1_RX_STA&0x4000) {
- UART1_RX_STA= (buf == 0x0a) ? (UART1_RX_STA| 0x8000) : 0;
- } else {
- // 接收到回车,将高第二位置1,否则继续接收数据
- if(buf == 0x0d) {
- UART1_RX_STA |= 0x4000;
- } else {
- UART1_RX_Buffer[UART1_RX_STA&0x3fff] = buf;
- UART1_RX_STA ++;
- if(UART1_RX_STA > UART1_REC_LEN - 1) {
- UART1_RX_STA = 0;
- }
- }
- }
- HAL_UART_Receive_IT(&huart1, &buf, 1);
- }
-
- int fputc(int ch, FILE *file) {
- unsigned char temp[1] = {ch};
- HAL_UART_Transmit(&huart1, temp, 1, 0xfff);
- return ch;
- }
-
- int main(void)
- {
- HAL_Init();
-
- SystemClock_Config();
-
- MX_GPIO_Init();
- MX_USART1_UART_Init();
- HAL_UART_Receive_IT(&huart1, &buf, 1);
- while (1)
- {
- if(UART1_RX_STA & 0x8000)
- {
- printf("收到数据:");
- if(UART1_RX_Buffer[0] == '\0') continue;
- if(!strcmp((const char *)UART1_RX_Buffer, "open")) {
- HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
- } else if(!strcmp((const char *)UART1_RX_Buffer, "close")){
- HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
- } else {
- printf("error code");
- }
- HAL_UART_Transmit(&huart1, UART1_RX_Buffer, UART1_RX_STA & 0x3fff, 0xffff);
- while(huart1.gState != HAL_UART_STATE_READY);
- UART1_RX_STA = 0;
- } else {
- printf("hello heart\r\n");
- }
- HAL_Delay(1000);
- }
- }
重写fputc函数就可以重定向printf,里面就是调用了HAL库中串口的发送函数。
- int fputc(int ch, FILE *file) {
- unsigned char temp[1] = {ch};
- HAL_UART_Transmit(&huart1, temp, 1, 0xfff);
- return ch;
- }
注意:这里需要在keil 选中Use Micro LIB,否则会不成功。

初始化完成之后和接收中断函数调用完成之后,需要重新调用接收中断函数
HAL_UART_Receive_IT(&huart1, &buf, 1);
这里定义一个uint16_t类型的UART1_RX_STA变量,其中
注意点:
- void MX_USART1_UART_Init(void)
- {
- huart1.Instance = USART1;
- huart1.Init.BaudRate = 9600;
- huart1.Init.WordLength = UART_WORDLENGTH_8B;
- huart1.Init.StopBits = UART_STOPBITS_1;
- huart1.Init.Parity = UART_PARITY_NONE;
- huart1.Init.Mode = UART_MODE_TX_RX;
- huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart1.Init.OverSampling = UART_OVERSAMPLING_16;
- if (HAL_UART_Init(&huart1) != HAL_OK)
- {
- Error_Handler();
- }
- }
Instance类型USART_TypeDef,主要是串口包含的寄存器,USART1是一个宏,绑定串口1的物理地址,方式和之前介绍的GPIO类似。
另外USART1 是挂载到APB2总线上,其它串口挂载到APB1总线上。
- typedef struct
- {
- __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */
- __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */
- __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
- __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
- __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
- __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
- __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
- } USART_TypeDef;
huart1 的类型是UART_HandleTypeDef,定义如下
- typedef struct __UART_HandleTypeDef
- {
- USART_TypeDef *Instance;
- UART_InitTypeDef Init;
- const uint8_t *pTxBuffPtr;
- uint16_t TxXferSize;
- __IO uint16_t TxXferCount;
- uint8_t *pRxBuffPtr;
- uint16_t RxXferSize;
-
- __IO uint16_t RxXferCount;
-
- __IO HAL_UART_RxTypeTypeDef ReceptionType;
- __IO HAL_UART_RxEventTypeTypeDef RxEventType;
- DMA_HandleTypeDef *hdmatx;
-
- DMA_HandleTypeDef *hdmarx;
- HAL_LockTypeDef Lock;
-
- __IO HAL_UART_StateTypeDef gState;
- __IO HAL_UART_StateTypeDef RxState;
- __IO uint32_t ErrorCode;
-
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
-
- void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);
- *******省略代码*******
- #endif /* USE_HAL_UART_REGISTER_CALLBACKS */
-
- } UART_HandleTypeDef;
Init类型如下,主要是设置数据传输的配置。
- typedef struct
- {
- uint32_t BaudRate;
- uint32_t WordLength;
- uint32_t StopBits;
- uint32_t Parity;
- uint32_t Mode;
- uint32_t HwFlowCtl;
- uint32_t OverSampling;
- } UART_InitTypeDef;
Init 主要配置波特率,字长,停止位等等。
其实HAL库中的模块配置都很相似,包括GPIO, 定时器等。
主要操作如下,代码已经添加了注释
- HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
- {
- //判断 是否地址无效
- if (huart == NULL)
- {
- return HAL_ERROR;
- }
- // 流控制位有效或无效时的操作
- if (huart->Init.HwFlowCtl != UART_HWCONTROL_NONE)
- {
- // 流控制器只适用于串口1 2 3
- assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));
- assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));
- }
- else
- {
- assert_param(IS_UART_INSTANCE(huart->Instance));
- }
- assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));
- #if defined(USART_CR1_OVER8)
- assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling));
- #endif /* USART_CR1_OVER8 */
-
- if (huart->gState == HAL_UART_STATE_RESET)
- {
- // 先解锁再操作
- huart->Lock = HAL_UNLOCKED;
-
- // 这里没有定义回调,只会走else
- #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
- UART_InitCallbacksToDefault(huart);
-
- if (huart->MspInitCallback == NULL)
- {
- huart->MspInitCallback = HAL_UART_MspInit;
- }
-
- huart->MspInitCallback(huart);
- #else
- //初始化GPIO,串口优先级等
- HAL_UART_MspInit(huart);
- #endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */
- }
-
- huart->gState = HAL_UART_STATE_BUSY;
-
- // 先关闭串口,再操作,最后打开串口
- __HAL_UART_DISABLE(huart);
-
-
- UART_SetConfig(huart);
-
- /* In asynchronous mode, the following bits must be kept cleared:
- - LINEN and CLKEN bits in the USART_CR2 register,
- - SCEN, HDSEL and IREN bits in the USART_CR3 register.*/
- //异步模式下,USART_CR2的LINEN(局域网模式)和CLKEN(时钟使能)要清0
- // USART_CR3的SCEN, HDSE, IREN ,STOP(文档里有说明)要清0
- CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));
- CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));
-
- __HAL_UART_ENABLE(huart);
-
- // 设置串口的初始化状态
- huart->ErrorCode = HAL_UART_ERROR_NONE;
- huart->gState = HAL_UART_STATE_READY;
- huart->RxState = HAL_UART_STATE_READY;
- huart->RxEventType = HAL_UART_RXEVENT_TC;
-
- return HAL_OK;
- }
UART_SetConfig 是串口的主要寄存器配置,核心代码如下
- static void UART_SetConfig(UART_HandleTypeDef *huart)
- {
- uint32_t tmpreg;
- uint32_t pclk;
- // 设置停止位
- MODIFY_REG(huart->Instance->CR2, USART_CR2_STOP, huart->Init.StopBits);
-
-
- // 根据Init.WordLength 配置USART1_CR1寄存器中各位
-
- #if defined(USART_CR1_OVER8)
- tmpreg = (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode | huart->Init.OverSampling;
- MODIFY_REG(huart->Instance->CR1,
- (uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE | USART_CR1_OVER8),
- tmpreg);
- #else
- tmpreg = (uint32_t)huart->Init.WordLength | huart->Init.Parity | huart->Init.Mode;
- MODIFY_REG(huart->Instance->CR1,
- (uint32_t)(USART_CR1_M | USART_CR1_PCE | USART_CR1_PS | USART_CR1_TE | USART_CR1_RE),
- tmpreg);
- #endif /* USART_CR1_OVER8 */
-
- // 配置USART1_CR3寄存器中的RTSE(RTS使能),CTSE(CTS使能)
- MODIFY_REG(huart->Instance->CR3, (USART_CR3_RTSE | USART_CR3_CTSE), huart->Init.HwFlowCtl);
-
- // 配置时钟
- if(huart->Instance == USART1)
- {
- pclk = HAL_RCC_GetPCLK2Freq();
- }
- else
- {
- pclk = HAL_RCC_GetPCLK1Freq();
- }
-
- // 配置波特率
- #if defined(USART_CR1_OVER8)
- if (huart->Init.OverSampling == UART_OVERSAMPLING_8)
- {
- huart->Instance->BRR = UART_BRR_SAMPLING8(pclk, huart->Init.BaudRate);
- }
- else
- {
- huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
- }
- #else
- huart->Instance->BRR = UART_BRR_SAMPLING16(pclk, huart->Init.BaudRate);
- #endif /* USART_CR1_OVER8 */
- }
pclk的获取,这里大致分析下它的实现
- uint32_t HAL_RCC_GetPCLK2Freq(void)
- {
- // HAL_RCC_GetHCLKFreq 最终获取的是系统核心时钟16000000hz
- // CFGR是时钟配置寄存器,这里要配置的是PPRE2,在11-13位,APB预分频(APB2)
- // RCC_CFGR_PPRE2 = 11 1000 0000 0000 正好对应11-13位
- // RCC_CFGR_PPRE2_Pos = 8
- // const uint8_t APBPrescTable[8U] = {0, 0, 0, 0, 1, 2, 3, 4};
- // APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos] 这个操作是获取分频系数
- // 获取之后将 16000000hz 相除
- return (HAL_RCC_GetHCLKFreq() >> APBPrescTable[(RCC->CFGR & RCC_CFGR_PPRE2) >> RCC_CFGR_PPRE2_Pos]);
- }
五、效果图


