文章目录
- 一 RS-232
- 二 RS485
- 三 Modbus
- 四 stm32多路超声波测距
- 4.1 设计方案
- 4.2 代码
- 参考资料
- 总结
实验要求
一. 采用stm32F103和HC-SR04超声波模块, 使用标准库或HAL库+ 定时器中断,完成1或2路的超声波障碍物测距功能。
1)测试数据包含噪声,程序需要进行滤波处理;将测距数值通过串口上传到上位机串口助手;
2)根据障碍物距离远近,控制一个蜂鸣器(可以用LED灯代替)发出频率不同的声音(或LED不同闪烁),即输出占空比变化的PWM波形;
3)在没有超声波模块硬件的场景下,先使用Keil中的仿真逻辑分析仪,观察分析对应管脚上的时序波形,判读是否符合协议规范。
二. 当前智能汽车上一般配置有12路超声波雷达,这些专用超声波雷达内置了MCU,直接输出数字化的测距结果,一般硬件接口采用串口RS485,通信协议采用modbus。请思考:
1)RS485与RS232(UART)有什么不同?
2)Modbus协议是什么?
3)如果让你设计一款 12路车载超声波雷达,采用 stm32F103+HC-SR04超声波模块,对外提供RS485和Modbus协议,你的设计方案是什么?
一 RS-232
是异步串行通信接口标准之一,规定了连接电缆和机械、电气特性、信号功能及发送过程
特点:
- 支持全双工通信
- 波特率可选
- 负逻辑传送
- 传送距离较远
缺点:
- 接口信号电平值较高,易损坏接口电路芯片
- 传输速率较低,只有20Kbps
- 共地传输,易产生共模干扰
- 传输距离有限
引脚定义
通信机理
A向B发送数据:
1、A先设置RTS为1,表示要发数据给B
2、B检测到RTS为1,先看自己是否准备好:
如果准备好,就设置CTS为1表示A可以发数据给B了
如果没有准备好,继续处理自己的数据。弄完了,再将CTS设置为1,让A发送数据
3、A发现CTS置1后,将数据通过TXD信号线发送出去
4、A每发送一次数据给B之前,都会继续上面的逻辑
5、A发送完数据后,就将RTS置0,表示数据发送完毕
二 RS485
为针对RS232的缺点,出现了RS485
半双工网络
特点
-
RS-485的电气特性:以两线间的电压差表示电平1和0
-
最高传输速率是10Mbps
-
平衡驱动器和差分接收器组合,抗共模干扰能力强,抗噪声干扰性好
-
RS232在总线上只允许连接1个收发器。RS485允许连接多达128个收发器,可使用单一的RS485建立设备网络
485驱动电路
RXD和TXD是连接串口的,choose是选择作为接收端还是发送端(二选一)。右边就输出A和B,即差分信号的线,与其它的485器件的AB连接。
三 Modbus
- Modbus是一种由Modicon(施耐德电气公司)于1979年开发的串行通信协议,主要用于可编程逻辑控制器(PLC)和其他工业电子设备之间的通信。
- Modbus网络遵循主/从模型,其中主站(Master)负责请求信息,而从站(Slave)提供信息。如下图所示。
地址标识:每个从设备都有一个唯一的地址标识。
信息交互:主站可以读取从站的内部寄存器,也可以向其写入信息。
四 stm32多路超声波测距
4.1 设计方案
这里假设设定四路超声波。设定4个输入捕获通道,当超声波模块echo收到回波后,即触发输入捕获上升沿,开始echo持续时间的计时,然后下降沿后开始使用公式进行换算,得到距离,通过串口发送出去。其中,着重要考虑以下问题;
- stm32的定时器资源是否足够,同一定时器可以采用多路记录超声波吗
可知stm32一个定时器有几个通道,这些通道可以用来进行输入捕获,此方案也节省了io口资源。但是需要考虑如何设定定时器计数频率,以及如何避免这种情况:假设定时器的重载值为200,echo在180时收到上升沿开始计数,会持续50个计数值,即在下一轮的30得到下降沿,此时如果直接使用减法,会造成负数或者不准确数值的问题。所以为了避免这种情况,需要使用一个变量来判断是否完成了一个计数周期。
- stm32的定时器之间是否会有计数冲突:
在实验检验过程中,单独测试一个通道时记录结果大致准确,但是接入两个模块时,似乎结果发生了干扰,但无法确认是否是定时器问题还是其它问题,后续有待解决。
- 若采用轮询(状态机)方案访问会出现当一个超声波停止工作时,其它超声波也无法正常工作的状态。
4.2 代码
GPIO定时器初始化
void GPIO_Init(void) {
// 配置Trig引脚为输出,Echo引脚为输入
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2; // Trig1, Trig2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_3; // Echo1, Echo2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Timer_Init(void) {
// 配置定时器用于捕获Echo信号
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
Modbus设计
void USART_Init(void) {
// 配置USART用于RS485通信
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // USART1_TX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // USART1_RX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
void Send_Modbus_Response(uint8_t* response, uint8_t length) {
// 发送Modbus响应帧
for (uint8_t i = 0; i < length; i++) {
USART_SendData(USART1, response[i]);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
}
参考资料
https://blog.csdn.net/LX567567/article/details/139182689?spm=1001.2014.3001.5502
https://mp.weixin.qq.com/s/6wBNP-9SHh1OGiTV51gH1w
总结
进一步了解了RS232及RS485,为后续设计打下基础。