版本:STM32F429 Hal库v1.10

串口通信能够实现两块电路之间不同的通信,在开发中作为打印调试也是一门利器(printf重定向)。

补充一点小知识:

  1. weak修饰符修饰的函数,说明这个函数如果在其他地方还有定义的话,则编译时使用其他地方定义的同名函数

  2. UNUSED(void x); 这个函数是防止编译器出现 未使用警告。

下面给出串口通信的具体步骤。

串口的初始化

1.声明串口属性结构体、并初始化(一般是作为全局变量)

typedef struct
{
USART_TypeDef *Instance;
  /*
    所使用的串口,值可以是
    USART2 、USART3 、UART4、UART5、
    UART7、UART8、USART1、USART6
  */
  UART_InitTypeDef Init;
  /* 串口通信参数结构体(附下表) */
    ...

  __IO HAL_UART_StateTypeDef    State;
  /* 
    串口当前状态(仅用于条件判断)
    HAL_UART_STATE_RESET    // 未初始化完毕
    HAL_UART_STATE_READY    // 就绪
    HAL_UART_STATE_BUSY    // 忙,处理中
    HAL_UART_STATE_BUSY_TX  // 忙于发送
    HAL_UART_STATE_BUSY_RX  // 忙于接收
    HAL_UART_STATE_BUSY_TX_RX// 忙于全双工通信
    HAL_UART_STATE_TIMEOUT  // 超时
    HAL_UART_STATE_ERROR   // 错误
  */
    __IO uint32_t    ErrorCode;        /* 错误时返回的编号 */

}UART_HandleTypeDef;
typedef struct
{
uint32_t BaudRate;
/* 波特率 */ uint32_t WordLength;
/*
  数据位长度,可以是:
    UART_WORDLENGTH_8B
    UART_WORDLENGTH_9B
*/
  uint32_t StopBits;
/*
  停止位
    UART_STOPBITS_1
    UART_STOPBITS_2
*/

uint32_t Parity;
/*
  校验位
  采用何种校验是事先规定好的。通常专门设置一个奇偶校验位,用它使这组代码中“1”的个数为奇数或偶数。
  若用奇校验,则当接收端收到这组代码时,校验“1”的个数是否为奇数,从而确定传输代码的正确性。
    UART_PARITY_NONE // 无
    UART_PARITY_EVEN // 偶校验
    UART_PARITY_ODD // 奇校验
*/
  uint32_t Mode;
/*
  收发模式
  UART_MODE_RX
  URAT_MODE_TX
  UART_MODE_TX_RX
*/   uint32_t HwFlowCtl;
/*
  硬件流控
  硬件流:RTS/CTS  (Request To Send/Clear To Send)即请求发送/清除发送协议,用于半双工时的收发切换
    UART_HWCONTROL_NONE
    UART_HWCONTROL_RTS
    UART_HWCONTROL_CTS
    UART_HWCONTROL_RTS_CTS
*/
uint32_t OverSampling;
/*
  过采样
  可配置的16倍过采样或8倍过采样,因此为速度容差与时钟容差的灵活配置提供了可能。
  UART_OVERSAMPLING_16
  UART_OVERSAMPLING_8
*/
}UART_InitTypeDef;

2.调用 HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart); 初始化

3.调用 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) ;

  开启接收中断(如果需要的话)

3.重写 void HAL_UART_MspInit(UART_HandleTypeDef *huart); 对IO口进行初始化

这一步包括配置IO口为复用模式,开启中断等。

//UART底层初始化,时钟使能,引脚配置,中断配置
//此函数会被HAL_UART_Init()调用
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_Initure; if(huart->Instance==USART1)          //如果是串口1,进行串口1 MSP初始化
{
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOA时钟
__HAL_RCC_USART1_CLK_ENABLE(); //使能USART1时钟 GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; //高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; //复用为USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA9 GPIO_Initure.Pin = GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA10 #if EN_USART1_RX_IT
HAL_NVIC_EnableIRQ(USART1_IRQn); //使能USART1中断通道
HAL_NVIC_SetPriority(USART1_IRQn,,); //抢占优先级3,子优先级3
#endif
}
}

串口读写&中断处理

// http://t.cn/RuljJ01

// 读取串口状态
HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart); // 从串口中接收字符(阻塞,具有毫秒级的超时管理机制)
// 如果超时没接收完成,则不再接收数据到指定缓冲区,返回超时标志(HAL_TIMEOUT)
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 开启串口接收中断
// 把 接收缓冲区指针 指向 要存放接收数据的数组,设置 接收长度,接收计数器初值,然后使能串口接收中断。接收到数据时,会触发串口中断。
// 再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,不再触发接收中断,调用串口接收完成回调函数 HAL_UART_RxCpltCallback() 。
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
 
// 进入DMA中断,接收串口数据(非阻塞)
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

/*
根据网上资料显示,HAL_UART_Receive_IT() 这个函数只能对串口中断接收进行一次接收,而且接收的字节大小是固定的uint16_t Size,
但是在实际使用中,不可能完全满足每次接收到的字节数都是一样的,而且是确定的。
所以大家采用的方法都是令 uint16_t Size = 1;这样的话,每接收到一个字节就中断一次。

  那么中断处理函数处理的规则应该是 
  1、关闭此接收中断 
  2、将接收到的数据转移至缓存器 
  3、再次打开中断

 */

// 串口发送数据(阻塞,具有毫秒级的超时管理机制)
// 如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 进入发送中断
// 把 发送缓冲区指针 指向 要发送的数据,设置 发送长度,发送计数器初值,然后使能串口发送中断,触发串口中断。
// 再然后,串口中断函数处理,直到数据发送完成,而后关闭中断,不再发送数据,串口发送完成触发回调函数:HAL_UART_TxCpltCallback()
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
 

// 进入DMA发送中断
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

中断处理

HAL_UART_TxHalfCpltCallback();   一半数据(half transfer)发送完成后,通过中断处理函数调用。
HAL_UART_TxCpltCallback();     发送完成后,通过中断处理函数调用。
HAL_UART_RxHalfCpltCallback();   一半数据(half transfer)接收完成后,通过中断处理函数调用。
HAL_UART_RxCpltCallback();     接收完成后,通过中断处理函数调用。
HAL_UART_ErrorCallback();      传输过程中出现错误时,通过中断处理函数调用。

STM32串口中断入口函数

USARTx_IRQHandler   // x 可以是1到6

HAL_UART_IRQHandler

可看到串口发送和就是有三种通信模式:
第一种是上面用到的轮询的模式。CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。
第二种就是中断控制方式。当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。
第三种就是直接内存存取技术(DMA)方式。所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。

在 HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 中再次调用 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size) 
这样的话,就可以实现连续中断接收USART数据。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 

if(huart->Instance == USART1) 

UART1RxBuff[UART1RxBuffCount++] = aRxBuffer; 

HAL_UART_Receive_IT(huart, (uint8_t *)&aRxBuffer, 1) ; 
}

串口收发例程

STM32学习笔记:【004】USART串口通信的更多相关文章

  1. (stm32f103学习总结)—USART串口通信

    一. USART简介 USART即通用同步异步收发器,它能够灵活地与外部设备进行全双工 数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求. UART即通用异步收发器,它是在USART基 ...

  2. 【STM32学习笔记】USART 硬件流控

    流控的概念源于 RS232 这个标准,在 RS232 标准里面包含了串口.流控的定义.大家一定了解,RS232 中的"RS"是Recommend Standard 的缩写,即&qu ...

  3. STM32学习笔记(五) USART异步串行口输入输出(轮询模式)

    学习是一个简单的过程,只要有善于发掘的眼睛,总能学到新知识,然而如何坚持不懈的学习却很困难,对我亦如此,生活中有太多的诱惑,最后只想说一句勿忘初心.闲话不多扯,本篇讲诉的是异步串行口的输入输出,串口在 ...

  4. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  5. STM32学习笔记(四)——串口控制LED(中断方式)

    目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...

  6. STM32学习笔记——OLED屏

    STM32学习笔记--OLED屏 OLED屏的特点: 1.  模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2.  显示尺寸为0.96寸 3.  分辨率为128*64 4.   ...

  7. STM32学习笔记-NVIC中断知识点

    STM32学习笔记-NVIC中断知识点总结 中断优先级设置步骤 1. 系统运行后先设置中断优先级分组 函数:void NVIC_PriorityGroupConfig(uint32_tNVIC_Pri ...

  8. STM32学习笔记——点亮LED

    STM32学习笔记——点亮LED 本人学习STM32是直接通过操作stm32的寄存器,使用的开发板是野火ISO-V2版本: 先简单的介绍一下stm32的GPIO: stm32的GPIO有多种模式: 1 ...

  9. stm32学习笔记——外部中断的使用

    stm32学习笔记——外部中断的使用 基本概念 stm32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个.比如说,PA0,PB0 ...

随机推荐

  1. 洛谷p1586四方定理题解

    题目 这个题的本质是动态规划中的背包问题. 为什么会想到背包呢. 因为往往方案数不是排列组合就是递推或者是dp,当然还有其他的可能.我们可以把一个数的代价当成这个数的平方,价值就是一个方案数.由于这个 ...

  2. 常用LaTeX随时更

    连乘 \prod_{i=1}^n \[\prod_{i=1}^n\] 分数 \frac{a}{b} \[\frac{a}{b}\] 组合数 \tbinom{n}{r}=\tbinom{n}{n-r}= ...

  3. 自学华为IoT物联网_03 公共事业物联网常见问题及解决方案

    点击返回自学华为IoT物流网 自学华为IoT物联网_03 公共事业物联网常见问题及解决方案 本文从以下六项公共事业,看看物联网的解决方案: 停车问题 路灯管理问题 消防栓管理问题 井盖管理问题 水表管 ...

  4. Android 程序优化总结

    第一部分 编程规范 1.1 基本要求: 程序结构清晰,简单易懂,单个函数的程序行数不得超过100行. 打算干什么,要简单,直接. 尽量使用标准库函数和公共函数 不要随意定义全局变量,尽量使用局部变量. ...

  5. 每天一个Linux命令(05):tail命令

    tail命令用于输入文件中的尾部内容.tail命令默认在屏幕上显示指定文件的末尾10行.如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题.如果没有指定文件或者文件名为"-&qu ...

  6. virtio,macvtap,sriov

    本文转自http://xiaoli110.blog.51cto.com/1724/1558984 一个完整的数据包从虚拟机到物理机的路径是: 虚拟机--QEMU虚拟网卡--虚拟化层--内核网桥--物理 ...

  7. Java中数组判断元素存在几种方式比较详解

    1. 通过将数组转换成List,然后使用List中的contains进行判断其是否存在 public static boolean useList(String[] arr,String contai ...

  8. 安装Ubuntu Server18.04(附与CentOS占用体积和Python版本的对比)

    这边只演示一下最新系统的安装过程,设置之类的和以前讲的Kali以及CentOS大同小异:https://www.cnblogs.com/dunitian/p/4822808.html#linux 和C ...

  9. OO第二阶段纪实

    $ 0 写在前面 往往是那些令人格外痛苦的经历,会带给人以最快的成长.转眼间,半个学期的时间过去了,时间匆匆,不管之前对这几次充满了怎样的畏惧,在身边朋友们的帮助和努力下,我也渐渐度过了一个个难关.回 ...

  10. jackson json转对象 对象转json

    一,Jackson使用示例 第1步:创建ObjectMapper对象. 创建ObjectMapper对象.它是一个可重复使用的对象. ObjectMapper mapper = new ObjectM ...