STM32的485

文章目录

  • RS232
    • COM接口
    • 232通信
    • 232电平
    • 传输距离
  • RS485
    • 485电平
    • RS485的两个电阻
    • RS485连接方式
    • SP3485芯片
  • 485通信实验
    • 实验介绍
    • Rs485.h
    • Rs485.c
    • 先初始化端口、串口和中断
      • F407初始化
      • F407 USART2 初始化设置
      • F407中断
      • F407 RS485 发送 len 个字节
    • 开启时钟
    • 串口初始化和中断初始化
    • RS485发送和接收数据
    • 使用逻辑
  • 485使用总结

鸣谢:爱上半导体、正点原子

RS232

RS即recommended standard推荐标准,是电子工业协会(Electronic Industries Association,EIA) 制定的异步传输标准接口。STM32的485_第1张图片

COM接口

STM32的485_第2张图片
一般只用三根线
STM32的485_第3张图片
这种接口的协议只有两种:RS-232和RS-485。不会是TTL电平。

232通信

事实上就是增加一个芯片,将TTL电平转换成232电平。如CH340芯片。
CH340T 是一个USB总线的转接芯片,可以实现USB转串口、USB 转IrDA 红外或者USB转打印口。
STM32的485_第4张图片
转换之后的电平
STM32的485_第5张图片
作用:抗干扰能力增强了

232电平

高电平:3-15V
低电平: -3~-15V
STM32的485_第6张图片

传输距离

STM32的485_第7张图片STM32的485_第8张图片

RS485

485(一般称作 RS485/EIA-485)是隶属于 OSI 模型物理层的电气特性规定为 2 线,半双工,多点通信的标准。

  • 注意一点:232是全双工,485是半双工。全双工只能点对点通讯,半双工则可以进行一主多从的通讯。是缺点也是优点。
  • 电气特性:用缆线两端的电压差值来表示传递信号。
    - 逻辑“1”以两线间的电压差为+(2~6)V表示;
    - 逻辑“0”以两线间的电压差为-(2~6)V 表示。
  • 特点
    • 接口电平低,不易损坏芯片。
    • 传输速率高。10 米时,RS485 的数据最高传输速率可达 35Mbps,在 1200m 时,传输速度可达 100Kbps。
    • 抗干扰能力强。RS485 接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好。
      • 差分信号抗干扰能力强是常识,因为干扰会对两根双绞线同时造成影响,差值不变,贼准。
    • 传输距离远,支持节点多。RS485 总线最长可以传输 1200m 以上(速率≤100Kbps)一般最大支持 32 个节点,如果使用特制的 485 芯片,可以达到 128 个或者 256 个节点,最大的可以支持到 400 个节点。

485电平

STM32的485_第9张图片

485芯片就是TTL电平和485电平的转换器
STM32的485_第10张图片
B-A,当信号A>B,压差为-(2~6)V,逻辑0
B-A,当信号B>A,压差为+(2~6)V,逻辑1

RS485的两个电阻

RS485需要 2 个匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为 120Ω)。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。
一般在总线的起止端加入,也就是主机和设备 4 上面各加一个 120Ω的匹配电阻。

RS485连接方式

STM32的485_第11张图片
STM32的485_第12张图片
上图可知:485只需要两根线就可以表示出差分信号。不需要地线。

SP3485芯片

战舰 STM32 开发板采用 SP3485 作为收发器,该芯片支持 3.3V 供电,最大传输速度可达10Mbps,支持多达 32 个节点,并且有输出短路保护。
STM32的485_第13张图片

其中,A、B为总线接口,用于连接 485 总线。
RO 是接收输出端,DI 是发送数据收入端,RE是接收使能信号(低电平有效),DE 是发送使能信号(高电平有效)。
将串口的TTL电平,经过该芯片变成了485电平。
TX发送,DI收入。反之

硬件连接图:
STM32的485_第14张图片

STM32F1 的串口 2 通过 P7 端口设置,连接到 SP3485,通过 STM32F1的 PD7 控制 SP3485 的收发,当 PD7=0 的时候,为接收模式;当 PD7=1 的时候,为发送模式。
需要注意,RS485_RE 信号和 DM9000_RST 共用 PD7,所以他们也不可以同时使用。
R19 和 R22 是两个偏置电阻,用来保证总线空闲时,A、B 之间的电压差都会大于 200mV(逻辑 1)。从而避免因总线空闲时,A、B 压差不定,引起逻辑错乱,可能出现的乱码。
然后,我们要设置好开发板上P7排针的连接,通过跳线帽将PA2和PA3分别连接到485_RX和 485_TX 上面
STM32的485_第15张图片

485通信实验

实验介绍

通过该芯片连接 STM32 的串口 2,实现两个开发板之间的 485 通信。
通过连接两个战舰 STM32 开发板的 RS485 接口,然后由 KEY0 控制发送,当按下一个开发板的 KEY0 的时候,就发送 5 个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。

Rs485.h

STM32的485_第16张图片

模式控制就是RE和DE

Rs485.c

同时 485 通信调用的库函数和定义分布在 stm32f10x_usart.c 文件和头文件 stm32f10x_usart.h文件中。
打开 rs485.c 文件,代码总共 4 个函数。

  • RS485_Init 函数为 485 通信初始化函数,其实基本上就是在配置串口 2,只是把 PD7 也顺带配置了,用于控制 SP3485 的收发。
  • 同时如果使能中断接收的话,会执行串口 2 的中断接收配置。
  • USART2_IRQHandler 函数用于中断接收来自 485 总线的数据,将其存放在 RS485_RX_BUF 里面。
  • 最后 RS485_Send_Data 和 RS485_Receive_Data 这两个函数用来发送数据到 485 总线和读取从 485 总线收到的数据,都比较简单。
#ifdef EN_USART2_RX //如果使能了接收
//接收缓存区
u8 RS485_RX_BUF[64]; //接收缓冲,最大 64 个字节.
//接收到的数据长度
u8 RS485_RX_CNT=0;
 
//bound:波特率
void RS485_Init(u32 bound)
{

先初始化端口、串口和中断

GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|
RCC_APB2Periph_GPIOG, ENABLE); //使能 GPIOA,G 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能串口 2 时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PG9 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStructure);
 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure);
 
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);

F407初始化

void RS485_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能 PA 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能 USART2 时钟
//串口 2 引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //PA2复用为USART2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);//PA3 复用为 USART2
//USART2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2 与 GPIOA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度 100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA2,PA3
//PG8 推挽输出,485 模式控制
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //GPIOG8
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度 100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOG,&GPIO_InitStructure); //初始化 PG8

F407 USART2 初始化设置

USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位
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(USART2, &USART_InitStructure); //初始化串口 2
USART_Cmd(USART2, ENABLE); //使能串口 2
USART_ClearFlag(USART2, USART_FLAG_TC);

F407中断

#if EN_USART2_RX
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启接受中断
//Usart2 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //响应优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 VIC 寄存器、
#endif
RS485_TX_EN=0; //默认为接收模式
}

F407 RS485 发送 len 个字节

//buf:发送区首地址
//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过 64 个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
u8 t;
RS485_TX_EN=1; //设置为发送模式
for(t=0;t<len;t++) //循环发送数据
{
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束
USART_SendData(USART2,buf[t]); //发送数据
}
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); //等待发送结束
RS485_RX_CNT=0;
RS485_TX_EN=0; //设置为接收模式
}
//RS485 查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=RS485_RX_CNT;
u8 i=0;
*len=0; //默认为 0
delay_ms(10); //等待 10ms,连续超过 10ms 没有接收到一个数据,则认为接收结束
if(rxlen==RS485_RX_CNT&&rxlen)//接收到了数据,且接收完成了
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //记录本次数据长度
RS485_RX_CNT=0; //清零
}
}

开启时钟


RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE); //复位串口 2
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止复位

串口初始化和中断初始化

USART_InitStructure.USART_BaudRate = bound; //波特率设置;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8 位数据长度
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(USART2, &USART_InitStructure); //初始化串口
 
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串口 2 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启中断
USART_Cmd(USART2, ENABLE); //使能串口
 
RS485_TX_EN=0; //默认为接收模式
}

RS485发送和接收数据

//RS485 发送 len 个字节.
//buf:发送区首地址
//len:发送的字节数(为了和本代码的接收匹配,这里建议不要超过 64 个字节)
void RS485_Send_Data(u8 *buf,u8 len)
{
u8 t;
RS485_TX_EN=1; //设置为发送模式
for(t=0;t<len;t++) //循环发送数据
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2,buf[t]);
}
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
RS485_RX_CNT=0;
RS485_TX_EN=0; //设置为接收模式
}
//RS485 查询接收到的数据
//buf:接收缓存首地址
//len:读到的数据长度
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=RS485_RX_CNT;
u8 i=0;
*len=0; //默认为 0
delay_ms(10); //等待 10ms,连续超过 10ms 没有接收到一个数据,则认为接收结束
if(rxlen==RS485_RX_CNT  & &   rxlen)//接收到了数据,且接收完成了
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //记录本次数据长度
RS485_RX_CNT=0; //清零
}
}

这个BUF是在中断中赋值的,中断之后才有缓冲。

使用逻辑

while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)//KEY0 按下,发送一次数据
{
for(i=0;i<5;i++)
{
rs485buf[i]=cnt+i;//填充发送缓冲区
LCD_ShowxNum(30+i*32,190,rs485buf[i],3,16,0X80); //显示数据
}
RS485_Send_Data(rs485buf,5);//发送 5 个字节
}
RS485_Receive_Data(rs485buf,&key);
if(key)//接收到有数据
{
if(key>5)key=5;//最大是 5 个数据.
for(i=0;i<key;i++)LCD_ShowxNum(30+i*32,230,rs485buf[i],3,16,0X80); //显示数据
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系统正在运行
t=0;
cnt++;
LCD_ShowxNum(30+48,150,cnt,3,16,0X80); //显示数据
}
}
}

此部分代码, cnt 是一个累加数,一旦 KEY0 按下按下,就以这个数位基准连续发送 5 个数据。当 485 总线收到数据的时候,就将收到的数据直接显示在 LCD 屏幕上。

485使用总结

  • 开启串口2和GPIOA和GPIOD的时钟
  • 设置GPIOD端口参数,初始化端口
  • 复位串口,设置串口参数,初始化串口,开启串口
  • 设置中断分组,设置中断优先级,初始化中断,开启中断
  • 485发送和接收数据

你可能感兴趣的:(嵌入式开发,stm32,单片机,arm,stm32)