概述
学习总线时曾经要求实现RS485通信,恰好跟前有两块带RS485的stm32开发板于是就简单实现了下。
先说下实现的功能,一块STM32F103板和STM32F407板通过RS485总线连接,F103板子采集温度发给F407屏幕显示出来,F407能够控制F103板子上的流水灯开关,然后F103能够将灯的实时状态发给F407显示,以此来实现双向通信。
下面直接贴关键代码
F103 main.c
double temp;
char isget;
char buf[8]; //接收缓存
char tbuf[8]; //发送缓存
void send(void);
int main()
{
int led=0;
int ttbuf;
LED_Init(); //初始化LED
rs485_init(); //初始化RS485配置
ds18b20_init(); //DS18B20初始化
isget=0;
tbuf[0]='S'; //起始帧
tbuf[1]=0x00; //地址
tbuf[2]=0x02; //命令
tbuf[7]='E'; //结束帧
GPIOC->ODR|=0x0080;
while(1)
{
if(isget)
{
isget=0;
if(buf[1]==0x01) //判断是该机地址
{
if(buf[2]==0x0A) //判断温度信号命令
{
temp=readtemp(); //读取温度
ttbuf=1000*temp; //温度去小数
tbuf[2]=0x02; //发送温度命令
//以下将 int型温度拆分成字节发送
tbuf[3]=ttbuf&0x000000ff;
tbuf[4]=(ttbuf>>8)&0x000000ff;
tbuf[5]=(ttbuf>>16)&0x000000ff;
tbuf[6]=(ttbuf>>24)&0x000000ff;
led_huayang(led<<7); //led信号灯
send(); //发送
}else if(buf[2]==0x0b) //判断控制信号
{
led_huayang(led<<7);
if(led==0)
{
GPIOC->ODR|=0x0080;
led=1;
}else{
GPIOC->ODR&=0xff7f;
led=0;
}
tbuf[2]=0x03; //返回信号
tbuf[3]=led; //返回灯的状态
send(); //发送灯的状态
}
}
}
}
}
int state=0,len=0;
void send()
{
int k;
GPIO_SetBits(GPIOG,GPIO_Pin_3);
delay_ms(1);
for(k=0;k<8;k++)
{
USART_SendData(USART2,tbuf[k]);
while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
}
delay_ms(2);
GPIO_ResetBits(GPIOG,GPIO_Pin_3);
}
void USART2_IRQHandler(void) //485通信中断函数
{
static u8 k;
USART_ClearFlag(USART2,USART_FLAG_TC);
if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)//检查指定的USART中断发生与否
{
k=USART_ReceiveData(USART2);
switch(k)
{
case 'S':
if(state==0)
{
state=1;
len=0;
}
break;
case 'E':
if(state==2)
{
state=0;
isget=1;
len=0;
}
break;
default:
if(state==1)
{
buf[1+len++]=k;
if(len==6)
state=2;
}
break;
}
}
}
F103 rs485.c
/*******************************************************************************
* 函 数 名 : rs485_init
* 函数功能 : IO端口及串口2,时钟初始化函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void rs485_init()
{
GPIO_InitTypeDef GPIO_InitStructure; //声明一个结构体变量,用来初始化GPIO
USART_InitTypeDef USART_InitStructure; //串口结构体定义
NVIC_InitTypeDef NVIC_InitStructure; //中断结构体定义
//打开时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
/* 配置GPIO的模式和IO口 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; //TX-485 //串口输出PA2
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3; //CS-485
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_3; //RX-485 //串口输入PA3
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
USART_InitStructure.USART_BaudRate = 9600; //波特率设置为9600 //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据长8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无效验
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //失能硬件流
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //开启发送和接受模式
USART_Init(USART2, &USART_InitStructure); /* 初始化USART2 */
USART_Cmd(USART2,ENABLE);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //使能或者失能指定的USART中断 接收中断
USART_ClearFlag(USART2,USART_FLAG_TC); //清除USARTx的待处理标志位
/* 设置NVIC参数 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //打开USART2的全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
}
F407 main.c
extern int temp;
extern int flag;
extern int led;
int main(void)
{
char*strbuf;
char buf[8]; //接收缓存
buf[0]='S'; //起始位
buf[1]=0x01; //地址
buf[7]='E'; //终止位
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(); //初始化延时函数
LED_Init(); //初始化LED
BEEP_Init(); //初始化蜂鸣器
LCD_Init(); //LCD初始化
KEY_Init(); //按键初始化
RS485_Init(9600); //初始化RS485串口2
BRUSH_COLOR=BLUE; //设置字体为红色
LCD_DisplayString(8,10,24,"Granary Temperature ");
LCD_DisplayString(8,40,24,"Test Control System");
BRUSH_COLOR=BLACK;
LCD_DisplayString(0,100,16,"Communication mode :RS485 ");
LCD_DisplayString(0,190,16,"Press down KEY2 to control LED"); //显示提示信息
LCD_DisplayString(0,130,16,"Real-time temperature :");
LCD_DisplayString(0,160,16,"LED state :");
buf[2]=0x0A; //设置温度命令
RS485_Send_Data(buf,8); //发送温度命令
buf[2]=0x0b; //设置控制命令
RS485_Send_Data(buf,8); //led命令
while(1)
{
key_scan(0);
if(flag==1){
delay_ms(300);
flag=0;
strbuf=float2str(temp/1000.0);
strbuf[6]='C';
strbuf[7]=0;
BRUSH_COLOR=RED;
LCD_DisplayString(184,130,16,(u8*)strbuf); //显示温度
buf[2]=0x0A; //温度命令
if(led==1)
LCD_DisplayString(184,160,16,"LED ON ");
else
LCD_DisplayString(184,160,16,"LED OFF");
RS485_Send_Data(buf,8); //温度命令
}
if(keyup_data ==KEY2_DATA)
{
buf[2]=0x0B; //LED命令
RS485_Send_Data(buf,8); //温度命令
LED2=!LED2;
}
}
}
F407 rs485.c
int temp;
int flag=0;
int led=0;
//接收缓存区
u8 RS485_receive_str[128]; //接收缓冲,最大128个字节.
u8 uart_byte_count=0; //接收到的数据长度
//初始化IO 串口2 bound:波特率
void RS485_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2时钟
//串口2引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2复用为USART2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3复用为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_6; //GPIOG6
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
//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);
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寄存器、
RS485_TX_EN=0; //初始化默认为接收模式
}
//串口2接收中断服务函数
int state=0;
void USART2_IRQHandler(void)
{
u8 rec_data;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)//接收到数据
{
rec_data =(u8)USART_ReceiveData(USART2); //(USART2->DR) 读取接收到的数据
if(rec_data=='S'&&state==0) //如果是S,表示是命令信息的起始位
{
state=1;
uart_byte_count=0x00;
}else if(rec_data=='E'&&state==2) //如果E,表示是命令信息传送的结束位处理数据
{
state=0;
if(RS485_receive_str[0]==0x00) //判断地址 地址正确
{
if(RS485_receive_str[1]==0x02) //接受温度数据
{
temp=RS485_receive_str[5]<<24|RS485_receive_str[2]|RS485_receive_str[3]<<8|RS485_receive_str[4]<<16;
flag=1;
} else if(RS485_receive_str[1]==0x03) //led控制回馈
{
led=RS485_receive_str[2];
}
}
}else if(state==1) //接收数据
{
RS485_receive_str[uart_byte_count++]=rec_data;
if(uart_byte_count==6)
state=2;
}
}
}
/****************************************************************************
* 名 称: void RS485_Send_Data(u8 *buf,u8 len)
* 功 能:RS485发送len个字节
* 入口参数:buf:发送区首地址
len:发送的字节数
* 返回参数:无
* 说 明:(为了和本代码的接收匹配,这里建议数据长度不要超过128个字节)
****************************************************************************/
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); //等待发送结束
uart_byte_count=0;
RS485_TX_EN=0; //发送完设置为接收模式
}
/****************************************************************************
* 名 称: void RS485_Receive_Data(u8 *buf,u8 *len)
* 功 能:RS485查询接收到的数据
* 入口参数:buf:接收缓存首地址
len:读到的数据长度
* 返回参数:无
* 说 明:
****************************************************************************/
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=uart_byte_count;
u8 i=0;
*len=0; //默认为0
delay_ms(10); //等待10ms,连续超过10ms没有接收到一个数据,则认为接收结束
if(rxlen==uart_byte_count&&rxlen) //接收到了数据,且接收完成了
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_receive_str[i];
}
*len=uart_byte_count; //记录本次数据长度
uart_byte_count=0; //清零
}
}
最后
以上就是激动小甜瓜为你收集整理的STM32F1和F4实现RS485简单双向通信的全部内容,希望文章能够帮你解决STM32F1和F4实现RS485简单双向通信所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复