刚刚入手stm32,并用HAL编程(看起来是面向对象)但还是对各种协议搅得找不着北,因此总结一下(不定期更新)
IIC协议
IIC(Inter-Integrated Circuit)其实是IICBus简称,所以中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。I²C的正确读法为“I平方C”(“I-squared-C”),而“I二C”(“I-two-C”)则是另一种错误但被广泛使用的读法。
- 硬件结构
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。设备上的串行数据线SDA接口电路应该是双向的,输出电路用于向总线上发送数据,输入电路用于接收总线上的数据。而串行时钟线也应是双向的,作为控制总线数据传送的主机,一方面要通过SCL输出电路发送时钟信号,另一方面还要检测总线上的SCL电平,以决定什么时候发送下一个时钟脉冲电平;作为接受主机命令的从机,要按总线上的SCL信号发出或接收SDA上的信号,也可以向SCL线发出低电平信号以延长总线时钟信号周期。总线空闲时,因各设备都是开漏输出,上拉电阻Rp使SDA和SCL线都保持高电平。任一设备输出的低电平都将使相应的总线信号线变低,也就是说:各设备的SDA是“与”关系,SCL也是“与”关系。
总线对设备接口电路的制造工艺和电平都没有特殊的要求(NMOS、CMOS都可以兼容)。在I2C总线上的数据传送率可高达每秒十万位,高速方式时在每秒四十万位以上。另外,总线上允许连接的设备数以其电容量不超过400pF为限。
总线的运行(数据传输)由主机控制。所谓主机是指启动数据的传送(发出启动信号)、发出时钟信号以及传送结束时发出停止信号的设备,通常主机都是微处理器。被主机寻访的设备称为从机。为了进行通讯,每个接到I2C总线的设备都有一个唯一的地址,以便于主机寻访。主机和从机的数据传送,可以由主机发送数据到从机,也可以由从机发到主机。凡是发送数据到总线的设备称为发送器,从总线上接收数据的设备被称为接受器。
I2C总线上允许连接多个微处理器以及各种外围设备,如存储器、LED及LCD驱动器、A/D及D/A转换器等。为了保证数据可靠地传送,任一时刻总线只能由某一台主机控制,各微处理器应该在总线空闲时发送启动数据,为了妥善解决多台微处理器同时发送启动数据的传送(总线控制权)冲突,以及决定由哪一台微处理器控制总线的问题,I2C总线允许连接不同传送速率的设备。多台设备之间时钟信号的同步过程称为同步化。
数据传输
在I2C总线传输过程中,将两种特定的情况定义为开始和停止条件(见图3):当SCL保持“高”时,SDA由“高”变为“低”为开始条件;当SCL保持“高”且SDA由“低”变为“高”时为停止条件。开始和停止条件均由主控制器产生。使用硬件接口可以很容易地检测到开始和停止条件,没有这种接口的微机必须以每时钟周期至少两次对SDA取样,以检测这种变化。
SDA线上的数据在时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,每次传输的字节不受限制,但每个字节必须要有一个应答ACK。如果一接收器件在完成其他功能(如一内部中断)前不能接收另一数据的完整字节时,它可以保持时钟线SCL为低,以促使发送器进入等待状态;当接收器准备好接受数据的其它字节并释放时钟SCL后,数据传输继续进行。I2C数据总线传送时序如图4所示。
数据传送具有应答是必须的。与应答对应的时钟脉冲由主控制器产生,发送器在应答期间必须下拉SDA线。当寻址的被控器件不能应答时,数据保持为高并使主控器产生停止条件而终止传输。在传输的过程中,在用到主控接收器的情况下,主控接收器必须发出一数据结束信号给被控发送器,从而使被控发送器释放数据线,以允许主控器产生停止条件。合法的数据传输格式如下:
I2C总线在开始条件后的首字节决定哪个被控器将被主控器选择,例外的是“通用访问”地址,它可以在所有期间寻址。当主控器输出一地址时,系统中的每一器件都将开始条件后的前7位地址和自己的地址进行比较。如果相同,该器件即认为自己被主控器寻址,而作为被控接收器或被控发送器则取决于R/W位。
HAL 库实现
main里对iic初始化
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
stm32f4xx_hal_msp.c配置文件
(该文件对引脚初始化)
void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hi2c->Instance==I2C1)
{
/* USER CODE BEGIN I2C1_MspInit 0 */
/* USER CODE END I2C1_MspInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**I2C1 GPIO Configuration
PB8 ------> I2C1_SCL
PB9 ------> I2C1_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_I2C1_CLK_ENABLE();
/* USER CODE BEGIN I2C1_MspInit 1 */
/* USER CODE END I2C1_MspInit 1 */
}
else if(hi2c->Instance==I2C2)
{
/* USER CODE BEGIN I2C2_MspInit 0 */
/* USER CODE END I2C2_MspInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**I2C2 GPIO Configuration
PB10 ------> I2C2_SCL
PB11 ------> I2C2_SDA
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_I2C2_CLK_ENABLE();
/* USER CODE BEGIN I2C2_MspInit 1 */
/* USER CODE END I2C2_MspInit 1 */
}
}
(这里我配置了两个IIC 方便接收数据,避免使用片选信号,以及传输不同步问题)
USART串口通信
USART(Universal synchronous asynchronous receivertransmitter )通用同步异步收发器,是STM32上基于串口通讯协议来实现与外部通信的一个外设,因为串口通讯协议的简单,便捷,所以在如今大多数的产品中都带支持串口通信,通过串口通信我们可以实现与单片机的通信,能够监控到我们MCU的收发数据,在进行产品的调试与开发时起着至关重要的作用。
在进行USART讲解之前,先讲解一下通讯的基本概念。
1、串行通信与并行通信
通讯按照数据传输的方式可分为串行通信与并行通信,串行通讯是指设备之间通过少量数据信号线(一般是 8根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用 8、16、32 及 64根或更多的数据线进行传输,数据位是同时传输的通讯方式,就好比这时有8辆车,串行通信就相当于只有一条马路8辆车一辆一辆过,而并行通信相当于有8条马路,8辆车同时通过
串行通信与并行通信的特性对比如下
由于并行传输对同步要求较高,且随着通讯速率的提高,信号干扰的问题会显著影响通讯性能,现在随着技术的发展,越来越多的应用场合采用高速率的串行差分传输。
2、全双工、半双工及单工
根据通讯方向的不同,通讯方式又可以分为全双工、半双工及单工三种方式。
全双工:在同一时刻允许两个设备的任何一个都可以同时进行数据的发送与接收,就好比双向车道,两个方向的车辆不会有任何的干扰。
半双工:在同一时刻,只允许两个设备进行接收或者发送,不允许在同一时刻单个设备既接收又发送,就好比在一条乡间小道上同一时刻只允许一辆车通过。
单工:在任何时刻,数据的传输方向都是不变的,数据只允许一个固定的方向传输,接收双方是固定的,一个固定为发送设备,一个固定为接收设备。就好比是单行道,只允许一个方向的行驶。
3、同步通讯与异步通讯
根据通讯的数据同步方式又分为同步和异步两种通讯方式。主要区别在于当数据传输时是否有使用到时钟信号来进行区分。
同步通讯:在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。
异步通讯:在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。
4、通讯速率
衡量通讯性能的一个非常重要的参数就是通讯速率,通常以比特率(Bitrate)来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易与比特率混淆的概念是“波特率”(Baudrate),它表示每秒钟传输了多少个码元。而码元是通讯信号调制的概念,通讯中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。如常见的通讯传输中,用 0V 表示数字 0,5V 表示数字 1,那么一个码元可以表示两种状态 0和 1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;如果在通讯传输中,有 0V、2V、4V 以及 6V 分别表示二进制数 00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率。
以上四个就是通讯的基本知识,下面开始就是USART的部分
文章开头有提到串口通讯是一种非常常用的通讯方式,所以想要使用串口通讯收发双方设备就需要符合串口通讯协议的物理规定也就是串口的机械电气特性,同时接受发送时也要符合规定通讯逻辑,统一收发双方的数据打包、解包标准。也就是要统一收发双方的物理层与协议层。
我们先来讲一下物理层,也就是实现串口通讯协议大多数用以下几种方式
1、RS232标准
2、USB转串口
3、原生的串口到串口
1、RS232标准串口通信结构图如下图所示
如果两个通讯设备之间使用的是RS232串行通讯协议标准,那么MCU出来的TTL电平信号先经过一个TTL转RS232电平转换芯片,然后经过一个DB9接口与外部设备进行连接(RS232串行通讯标准COM口俗称DB9接口),再经过一个电平转换芯片转换成CPU可以识别的TTL电平信号来实现通讯。为什么要使用电平转换芯片呢?是因为TTL电平与RS232电平不兼容,RS232接口任何一条信号线的电压均为负逻辑关系,即
RS232:+3v ~ +15v为二进制的逻辑0,(-3)v ~ (-15)v为二进制的逻辑1,
TTL:+2.4 ~ +5v为逻辑1, 0 ~ +0.5为逻辑0
RS232因为其抗干扰能力强一般用于工业通信而TTL当通讯距离大于40cm时,传输数据就会收到干扰。
DB9标准的公头与母头的接法如下图所示
实物如下图下图所示
2、USB转串口的串行通信方式
USB转串口的串行通讯协议主要用于MCU与PC间的通讯,并且在通讯时,PC要安装电平转换芯片的驱动。
3、原生串口到串口的串行通讯方式
原生的串口通信主要是控制器跟串口的设备戒者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信,比如GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块等。原理图如下
使用HAL库不需要了解USART底层寄存器原理,因此略过
USART校验控制
STM32F4xx 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将是 8位的数据帧加上 1位的校验位总共 9位,此时 USART_CR1寄存器的 M 位需要设置为1,即 9 数据位。将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存器的 PE 位置 1,并可以产生奇偶校验中断。使能了奇偶校验控制后,每个字符帧的格式将变成:起始位+数据帧+校验位+停止位。
USART中断控制
USART 有多个中断请求事件,具体如下图所示。
代码解析
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200; //波特率
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();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
stm32f4xx_hal_msp.c配置文件
(该文件对引脚初始化)
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA10 ------> USART1_RX
PA15 ------> USART1_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
至此IIC数据采集 USART串口通信就搭建好了
如果要在电脑端打印数据,需要借助于串口调试助手并重定向printf函数
int fputc(int ch,FILE*f)
{
HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,HAL_MAX_DELAY);
return ch;
}
该函数将数据一位一位传输并重写printf函数,使开发者很容易实现打印数据功能
至此 IIC数据采集USART串口通信框架便建立起来了,其实这里的很多函数都是由CubeMX软件自动生成 ,只需要重写printf函数就可以实现