找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 836|回复: 1
打印 上一主题 下一主题
收起左侧

求指点STM32 RS485发送帧数据更好的方法 附源程序

[复制链接]
跳转到指定楼层
楼主
不知道是不是因为我的方法不合理,使用了while()循环来485发送帧数据的时候,数码管会闪烁,普通串口不会,不知道问题是不是在while()这里。

//485收发上图:

贴代码(stm32f103ret6):
//main.c
#include "stm32f10x.h"                  // Device header
#include "LED.H"
#include "DISPLAY.H"
#include "TIMER.H"
#include "USART.H"
#include "RS485.H"

int main(void)
{
        LED_Init();
        DISPLAY1_Init();
        KEY_Init();
        INTERRUPT_Init();
        Timer_Init();
        USART1_Init();
        RS485_Init();
        
        while (1)
        {
               
        }        
}



//rs485.h
#ifndef __RS485_H
#define __RS485_H

void RS485_Init(void);

extern unsigned int Uart4_Rx;
extern unsigned char Uart4_Buffer[ ];
extern uint16_t Uart4_Tx_Num;

#endif


//rs485.c
#include "stm32f10x.h"                  // Device header
#include "DISPLAY.H"
        //RS485(UART4) : TX/PC10; RX/PC11; DE/PA12。
void RS485_Init(void)
{
                //GPIO_配置UART4_DE(PA12);并初始化为低电平,UART4_485为接收模式。
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);        //开启对应GPIO_时钟
        GPIO_InitTypeDef GPIO_InitStructure;        //定义GPIO_结构体
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;  
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;               
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        GPIO_ResetBits(GPIOA, GPIO_Pin_12);        //初始化UART4_DE为低电平,UART4_485默认为接收模式。
                //GPIO_配置UART4_TX(PC10)
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);        //开启对应GPIO_时钟
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOC, &GPIO_InitStructure);        
                //GPIO_配置UART4_RX(PC11)
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_Init(GPIOC, &GPIO_InitStructure);
        
                //USART_配置UART4(波特率9600,长度8字节,1停止位,0奇偶校验位,0硬件流控制)
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);        //开启对应USART_时钟
        USART_InitTypeDef USART_InitStructure;        //定义USART_结构体
        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(UART4, &USART_InitStructure);
        USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);        //接收中断,使能
        USART_ITConfig(UART4, USART_IT_IDLE, ENABLE);        //空闲总线中断,使能
        USART_Cmd(UART4, ENABLE);        //使设定的配置生效,但在STM32里,需要配合对应的时钟使用
        
                //NVIC_配置UART4(优先级分组2,抢占优先级为1,响应优先级为1)
        NVIC_InitTypeDef NVIC_InitStructure;        //定义NVIC_结构体
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                                   
        NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;  
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;        
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
        NVIC_Init(&NVIC_InitStructure);                                 
}

#define Max_BUFF_Len_4 18
unsigned char Uart4_Buffer[ Max_BUFF_Len_4 ];

unsigned int Uart4_Rx = 0;
void UART4_ReceiveData(void)
{
        if(USART_GetITStatus (UART4,USART_IT_RXNE))                //接收中断
        {
                Uart4_Buffer[ Uart4_Rx ] = USART_ReceiveData(UART4);
                Uart4_Rx ++;
                if(Uart4_Rx >=201)        //防止达数组上限
                {
                        Uart4_Rx = 0;
                }
        }
}

uint16_t Uart4_Tx_Num =0;
void UART4_TX(void)
{
        static uint32_t Clear;
        if(USART_GetITStatus (UART4,USART_IT_IDLE))                //总线空闲中断   
        {
                while(Uart4_Rx >=1)                //485循环发送数组Uart4_Buffer[ ]内的数,解决只一次发送的问题
                {
                        GPIO_SetBits(GPIOA, GPIO_Pin_12);        //485发送使能
                        USART_SendData(UART4,Uart4_Buffer[ Uart4_Tx_Num ]);
                        Uart4_Tx_Num ++;        //485发送顺序从数组Uart4_Buffer[ 0-100 ]
                        while(USART_GetFlagStatus (UART4,USART_FLAG_TC) !=1);
                        if(Uart4_Tx_Num == Uart4_Rx )        //        待发送完成
                        {
                                Uart4_Rx = 0;
                                Uart4_Tx_Num = 0;
                        }
                }
                if(Uart4_Rx ==0)
                {
                        Clear = UART4->SR;        //跳出总线空闲中断
                        Clear = UART4->DR;        //跳出总线空闲中断
                        GPIO_ResetBits(GPIOA, GPIO_Pin_12);
                }
        }
}

void UART4_IRQHandler(void)
{
        UART4_ReceiveData();
        UART4_TX();
        
        USART_ClearFlag (UART4,USART_FLAG_TC);
}



//usart.h
#ifndef __USART_H
#define __USART_H

void USART1_Init(void);

extern unsigned int Usart1_Rx;
extern unsigned char Usart1_Buffer[ ];


#endif


//usart.c
#include "stm32f10x.h"   

        //USART1 : TX/PA9; RX/PA10。
void USART1_Init(void)
{
                //GPIO_配置USART1_TX(PA9)               
        RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE );          //GPIOA时钟开启
        GPIO_InitTypeDef GPIO_InitInstructure;        
        GPIO_InitInstructure.GPIO_Mode = GPIO_Mode_AF_PP;          //选择引脚模式,TX 需配置为:_AF_PP(复用推挽输出,用作串口的输出)
        GPIO_InitInstructure.GPIO_Pin = GPIO_Pin_9;          //选择引脚
        GPIO_InitInstructure.GPIO_Speed = GPIO_Speed_50MHz;          //选择引脚频率
        GPIO_Init(GPIOA,&GPIO_InitInstructure );        //进行参数初始化
                //GPIO_配置USART1_RX(PA10)
        GPIO_InitInstructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;          //选择引脚模式,RX 需配置为:_IN_FLOATING(浮空输入,由于其输入阻抗比较大,一般把这种模式用于标准的通讯协议,比如IIC、USART等)
        GPIO_InitInstructure.GPIO_Pin = GPIO_Pin_10;          //选择引脚
        GPIO_InitInstructure.GPIO_Speed = GPIO_Speed_50MHz;          //RX引脚,也可以不配置引脚频率
        GPIO_Init(GPIOA,&GPIO_InitInstructure );        //进行参数初始化
        
                //USART_配置USART1(波特率9600,长度8字节,1停止位,0奇偶校验位,0硬件流控制)
        RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1 ,ENABLE );        //串口USART1时钟开启
        USART_InitTypeDef USART_InitInstructure;
        USART_InitInstructure.USART_BaudRate = 9600;        //直接修改波特率即可
        USART_InitInstructure.USART_WordLength = USART_WordLength_8b;        //长度 8字节
        USART_InitInstructure.USART_StopBits = USART_StopBits_1;        //1位 停止位
        USART_InitInstructure.USART_Parity = USART_Parity_No;        //无 奇偶校验位
        USART_InitInstructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;        //无 硬件流控制
        USART_InitInstructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //使能发送与接收功能
        USART_Init(USART1,&USART_InitInstructure);
        USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);        //接收中断,使能
        USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);        //总线空闲中断,使能               
        USART_Cmd (USART1 ,ENABLE );        //使设定的配置生效,但在STM32里,需要配合对应的时钟使用

                //NVIC_配置USART1(优先级分组2,抢占优先级为2,响应优先级为2)
        NVIC_InitTypeDef NVIC_InitStructure;        
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;        //从stm32f10x.h里第243-268行选择
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;        //抢占优先级为2
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;        //响应优先级为2
        NVIC_Init(&NVIC_InitStructure);          //进行参数初始化
}

#define Max_BUFF_Len_1 18
unsigned char Usart1_Buffer[ Max_BUFF_Len_1 ];

unsigned int Usart1_Rx = 0;

void USART1_IRQHandler(void)
{
        if(USART_GetITStatus (USART1,USART_IT_RXNE))        //接收中断
        {
                Usart1_Buffer[ Usart1_Rx ] = USART_ReceiveData(USART1);
                Usart1_Rx ++;
               
                USART_SendData(USART1,Usart1_Buffer[ Usart1_Rx -1 ]);  //接收完立即发
               
                Usart1_Rx = 0;
        }
        
        USART_ClearFlag (USART1,USART_FLAG_TC);
}



//TIMER.H
#ifndef __TIMER_H
#define __TIMER_H

void Timer_Init(void);
void TIM2_IRQHandler(void);

extern uint16_t k;


#endif


//TIMER.c
#include "stm32f10x.h"                  // Device header
#include "DISPLAY.H"

void Timer_Init(void)
{
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);        //开启TIM2时钟
        
        TIM_InternalClockConfig(TIM2);        //
        //计数器溢出频率CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)
        //内部时钟默认为72M,此处选用内部时钟频率的话
        //若设置为72M/(7200-1 +1) /(10000-1 +1) = 1秒,即:1秒进中断一次。
        //结构体(TIM_TimeBaseInitStructure),在本函数内定义一次即可
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;         //
        TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;          //滤波采样分频系数,1/2/4分频。它影响的是滤波器的采样(输入)频率,与输出无关
        TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式
        TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;          //定时器预分频器设置,可设置范围为 0 至 65535,实现 1 至 65536 分频,内部自动+1。        
        TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;        //定时器周期,设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为 0 至 65535,内部自动+1。
        TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;        //高级定时器才有的。周期倍数:n+1倍。此处为0时,即1倍周期1s;为1时即2倍周期2s,n最大为225(0XFF)。
        TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);         //初始化参数(TIM)
        
        TIM_ClearFlag(TIM2, TIM_FLAG_Update);        //清除更新中断,避免一打开中断就立刻产生中断
        TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);         //打开定时器更新中断
        
                //前提条件1:组别优先顺序(第0组优先级最强,第4组优先级最弱):NVIC_PriorityGroup_0>NVIC_PriorityGroup_1>NVIC_PriorityGroup_2>NVIC_PriorityGroup_3>NVIC_PriorityGroup_4
                //前提条件2:不同组优先级别间,依据优先级强弱,优先级别高的组,其中断源可以打断优先级别低的组的正在做的事情;(即:不同组优先级间,可以中断嵌套)
                //前提条件3:组优先级别>抢占优先级别>响应优先级别
                //前提条件4:抢占优先级越小,优先级越高;但同组优先级不能相互打断,也就没有中断嵌套
                //前提条件5:响应优先级越小的中断首先执行(不能嵌套),如果响应优先级也均相同,则根据各中断对应向量表的位置来确定,向量表中越靠前的中断先响应
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);          //函数优先级分组配置,共5组(0-4),此处为第1组。
        
        NVIC_InitTypeDef NVIC_InitStructure;
        NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;          //选择EXTI2中断 ,对应之前开启的TIM2时钟
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //使能EXTI2中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;        //抢占优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         //响应优先级
        NVIC_Init(&NVIC_InitStructure);          //初始化参数(NVIC)
        
        TIM_Cmd(TIM2, ENABLE);         //使能TIM2外设
}

uint16_t k = 1;

void TIM2_IRQHandler(void)
{
        if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)        //检查指定的TIM中断发生与否
        {
                k ++;
                if(k >= 40)
                {
                        k = 1;
                }
                Display();
               
                TIM_ClearITPendingBit(TIM2, TIM_IT_Update);                //清除TIM2的中断待处理位
        }
}



//display.h
#ifndef __DISPLAY_H
#define __DISPLAY_H

void DISPLAY1_Init(void);
void Display(void);


extern uint16_t n;
extern uint16_t p;

#endif


//display.c
#include "stm32f10x.h"                  // Device header
#include "TIMER.H"
#include "USART.H"
#include "RS485.H"

void DISPLAY1_Init(void)        //初始化时可以设置为默认完全打开功能,检测硬件是否OK
{
        //PB4:SGE1(位选1);PB3:SEG2(位选2)
        //PC13:a;PC14:b;PC15:c;PC1:d;
        //PC3:e;PA2:f;PA3:g;PC0:dp。
        //GPIO_Write(GPIOC, 0xE00B);
        //GPIO_Write(GPIOA,0x000C);
        //共阴数码管:所有阴极(负极)连在(并在)一起。共阳同理。        
                //结构体:
        GPIO_InitTypeDef GPIO_InitStructure;        //定义结构体GPIO_InitStructure,用它来传递数据
                //STM32F10x系列的MCU复位后,PA13/14/15 & PB3/4默认配置为JTAG功能。有时我们为了充分利用MCU I/O口的资源,会把这些端口设置为普通I/O口
               
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);        //使能相应GPIOB端口时钟以及AFIO功能时钟(复用时钟),在使用时开启,一方面可以降低功耗;
        GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);         
                //枚举类型:规范、限制变量赋值、取值范围,使其不能随意取值。
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;        //选择引脚模式,结构体GPIO_InitStructure内枚举类型GPIO_Mode赋值        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4 ;        //选择引脚,结构体GPIO_InitStructure内枚举类型GPIO_Pin赋值
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;        //选择引脚频率,结构体GPIO_InitStructure内枚举类型GPIO_Speed赋值
        GPIO_Init(GPIOB,&GPIO_InitStructure);        //进行参数初始化(GPIOB), &:取地址符号,这里使用地址传递
        
//        GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4);         //赋值,数码管高电平有效,非初始化必需步骤
        
        RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC ,ENABLE );
        GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
        GPIO_InitStructure .GPIO_Pin =GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3;
        GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz ;
        GPIO_Init (GPIOC,&GPIO_InitStructure );
        
//        GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3|GPIO_Pin_15|GPIO_Pin_14|GPIO_Pin_13);        //高电平有效
        
        RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA,ENABLE );
        GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP ;
        GPIO_InitStructure.GPIO_Pin =GPIO_Pin_2|GPIO_Pin_3 ;
        GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
        GPIO_Init(GPIOA,&GPIO_InitStructure);
        
//        GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3);
}

uint16_t p;
uint16_t n;
        //通过定时器内k(1-40)的自增循环,来进行数码管的点亮刷新
void Display(void)
{
        if(k >= 1 && k <= 19 )
        {
                GPIO_Write(GPIOB, 0x0010 );  //数码管位选1
        }
        if(k >= 20 && k <= 39)
        {
                GPIO_Write(GPIOB, 0x0008 );  //数码管位选2
        }                                
                //0-9
        const uint16_t num_table_1[] = {0xE00A,0xC000,0x600A,0xE002,0xC000,0xA002,0xA00A,0xE000,0xE00A,0xE002};                //,0x0001
        const uint16_t num_table_2[] = {0x0004,0x0000,0x0008,0x0008,0x000C,0x000C,0x000C,0x0000,0x000C,0x000C};                //,0x0000
                p= 1;//Uart4_Rx / 10 %10 ;
                n= 2;//Uart4_Rx % 10;
        if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_4) == 1)
        {
                GPIO_Write(GPIOC, num_table_1[p] );
                GPIO_Write(GPIOA, num_table_2[p] );                        
        }
        if(GPIO_ReadInputDataBit (GPIOB ,GPIO_Pin_3) == 1)
        {
                GPIO_Write(GPIOC, num_table_1[n] );
                GPIO_Write(GPIOA, num_table_2[n] );                        
        }
}
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:301191 发表于 2022-3-12 01:35 | 只看该作者
顶一下
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表