STM32F1和F4实现RS485简单双向通信

学习总线时曾经要求实现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//循环发送数据
    {
      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//记录本次数据长度
        uart_byte_count=0;        //清零
    }
}

你可能感兴趣的:(嵌入式开发,嵌入式开发与控制算法)