资讯专栏INFORMATION COLUMN

基于UCOSII的RS485通信(STM32F107)

verano / 3170人阅读

摘要:为了可靠工作,在总线状态切换时需要做适当延时,再进行数据收发。

一、实现效果

        基于ucosii实时操作系统的RS485通信,采用USART + DMA进行收发,

 二、开发环境

  • 开发工具:KEIL V5
  • 开发板: STM32f107RC
  • 采用方式:USART + DMA
  • 使用系统:UCOSII

三、RS485部分原理

        在RS-485通讯网络中,节点中的串口控制器使用RXTX信号线连接到收发器上,而收发器通过差分线连接到网络总线,串口控制器与收发器之间一般使用TTL信号传输,收发器与总线则使用差分信号来传输。

        发送数据时,串口控制器的TX信号经过收发器转换成差分信号传输到总线上,

        而接收数据时,收发器把总线上的差分信号转化成TTL信号通过RX引脚传输到串口控制器中。

        MCU管脚输出TTL电平,TTL电平的意思是,当MCU管脚输出0电平时,一般情况下电压是0V,当MCU管脚输出1电平时,电压是5V。因TTL电平的是由一条信号线,一条地线产生,信号线上的干扰信号会跟随有效信号传送到接收端,使得有效信号受到干扰,485通讯实际上是把MCU出来的TTL电平通过硬件层的一个转换器芯片进行转换

        RS-485通讯网络的最大传输距离可达1200米,总线上可挂载128个通讯节点,而由于RS-485网络只有一对差分信号线,它使用差分信号来表达逻辑,AB两线间的电压差为-6V~-2V时表示逻辑1,当电压差为+2V~+6V表示逻辑0,在同一时刻只能表达一个信号,所以它的通讯是半双工形式的。

        在单个实验板中,作为串口控制器的STM32USART外设引出TXRX两个引脚与RS-485收发器MAX485相连,收发器使用它的AB引脚连接到RS-485总线网络中。为了方便使用,我们每个实验板引出的AB之间都连接了1120欧的电阻作为RS-485总线的端电阻,所以要注意如果要把实验板作为一个普通节点连接到现有的RS-485总线时,是不应添加该电阻的

        MAX485芯片中有"RE"和"DE"两个引脚,用于控制485芯片的收发工作状态的,当RE引脚为低电平时,485芯片处于接收状态,当DE引脚为高电平时芯片处于发送状态。实验板中使用了STM32PD11直接连接到这两个引脚上,所以通过控制PD11的输出电平即可控制485的收发状态。

 实验板之间AA连接,BB连接即可

四、配置操作

 建立了5个任务

  任务名                                                 优先级
            APP_TASK_START_PRIO              2            主任务              
            Task_Com4_PRIO                           4            COM4通信任务
          
         当然还包含了系统任务:
            OS_TaskIdle                  空闲任务-----------------优先级最低
            OS_TaskStat                  统计运行时间的任务-------优先级次低 

 4.1 主任务建立

 //建立主任务, 优先级最高  建立这个任务另外一个用途是为了以后使用统计任务os_err = OSTaskCreate((void (*) (void *)) App_TaskStart, (void *) 0,   //指向任务代码的指针                                                                   (void *) 0,    //任务开始执行时,传递给任务的参数的指针                                                      (OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE - 1], //分配给任务的堆栈的栈顶指针   从顶向下递减                  (INT8U) APP_TASK_START_PRIO);    //分配给任务的优先级  
static  void App_TaskStart(void* p_arg) {   (void) p_arg    //使能ucos 的统计任务   #if (OS_TASK_STAT_EN > 0)    //----统计任务初始化函数     OSStatInit();                                      /* Determine CPU capacity.   */                          #endif      //建立其他的任务     App_TaskCreate();   while (1)   {       	  //1秒一次循环      OSTimeDlyHMSM(0, 0,1, 0);    } }

4.2 其他任务建立

static  void App_TaskCreate(void)  {     //CPU_INT08U os_err;		    //Com1_SEM=OSSemCreate(1);            //建立串口4中断的信号量    Com4_MBOX = OSMboxCreate((void *) 0);             //建立串口4中断的消息邮箱          //串口4接收及发送任务---------------------------------------------------------         OSTaskCreateExt(Task_Com4,                    //指向任务代码的指针                        (void *)0,                 //任务开始执行时,传递给任务的参数的指针                     (OS_STK *)&Task_Com4Stk[Task_Com4_STK_SIZE-1],//分配给任务的堆栈的栈顶指针   从顶向下递减                     Task_Com4_PRIO,               //分配给任务的优先级					 Task_Com4_PRIO,               //预备给以后版本的特殊标识符,在现行版本同任务优先级                     (OS_STK *)&Task_Com4Stk[0],   //指向任务堆栈栈底的指针,用于堆栈的检验                     Task_Com4_STK_SIZE,           //指定堆栈的容量,用于堆栈的检验                     (void *)0,                   //指向用户附加的数据域的指针,用来扩展任务的任务控制块				     OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR); //选项,指定是否允许堆栈检验,是否将堆栈清0,任务是否要进行浮点运算等等。 }

4.2.1 建立子任务——串口通信的任务

串口通信的任务:这里采用消息邮箱进行消息传递,

  • 在建立其他任务App_TaskCreate(void)的开始就首先建立串口的消息邮箱:Com4_MBOX=OSMboxCreate((void *) 0);
  • 然后在串口通信的任务中进入循环后就一直等待消息邮箱的信息(第8行),如果没有消息过来就一直等待,在此期间其他任务可以进行,一旦有消息发送过来,由于串口通信的优先级较高,就能很快响应,根据收到的消息msg来自定义。
  • 这里因为串口接收要用到中断,所以下面就说说串口通信的接收中断部分。
static  void Task_Com4(void *p_arg){         INT8U err;		 int i;    unsigned char * msg;     (void)p_arg;                             while(1)	{                 //OSSemPend(Com1_SEM,0,&err);          //等待串口接收指令成功的信号量       msg=(unsigned char *)OSMboxPend(Com4_MBOX, 0,&err);         //等待串口接收指令成功的邮箱信息		        //输出邮箱信息的前10个数据      if(msg != NULL)	  {			for(i = 0; i < 2; i++)			{				G_u8Usart1SendBuf[i] = 0x10;			}								USART_DMA_SendStart(DMA2_Channel5, 2);								memcpy(G_u8Usart1SendBuf, msg, 10);								USART_DMA_SendStart(DMA2_Channel5, 10);		}	    //DealWith_Data(pfifo); //处理数据    }  }

        以下是串口中断函数,接收串口数据,当发现是完整的帧时,就调用OSMboxPost(Com4_MBOX,(void *)&msg);发送一个邮箱消息,进而那边的串口任务从挂起到唤醒,执行相应的过程。

使用ringbuffer实现任意数据类型的FIFO处理接收数据,可以参考:stm32f0串口 DMA 空闲中断接收——基于HAL库(代码篇)_噗噗bug博客-CSDN博客

void UART4_IRQHandler(void){  uint16_t t;	unsigned int i;	unsigned char msg[50];	OS_CPU_SR  cpu_sr;		OS_ENTER_CRITICAL()     //保存全局中断标志,关总中断/	OSIntNesting++;		OS_EXIT_CRITICAL();  //恢复全局中断标志		if(USART_GetITStatus(UART4,USART_IT_IDLE) == SET)          //检查中断是否发生	{			RS485_TX_EN = 0;   				DMA_Cmd(DMA2_Channel3,DISABLE);                         //关闭DMA传输        DMA_ClearFlag( DMA2_FLAG_TC3 );  		t = DMA_GetCurrDataCounter(DMA2_Channel3);              //获取剩余数量				//FIFO_Add(pfifo, G_u8Usart1RecvBuf, UART4_RECV_MAXLEN - t); //fifo数据保存				memcpy(msg, G_u8Usart1RecvBuf, UART4_RECV_MAXLEN - t);	    OSMboxPost(Com4_MBOX,(void *)&msg);				DMA_SetCurrDataCounter(DMA2_Channel3,UART4_RECV_MAXLEN);   //重新设置传输的数量				 		DMA_Cmd(DMA2_Channel3,ENABLE);                      //开启DMA传输		USART_ReceiveData(UART4);                           //读一次数据,不然会一直进中断		USART_ClearFlag(UART4,USART_FLAG_IDLE);             //清除串口中断标志	}		OSIntExit();}

4.3 硬件初始化部分 

void BSP_Init(void){   /* NVIC configuration */   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    RS485_Config();   uart_init(9600);   delay_init();       //延迟函数初始化}

 4.4 RS485发送函数

注意:在485芯片的通信中,尤其要注意对485控制端DE的软件编程。为了可靠工作,在485总线状态切换时需要做适当延时,再进行数据收发。具体的做法是

     在数据发送状态下,   先将控制端置“1”,延时1ms左右的时间,在发送有效的数据,一包数据发送结束后再延时1ms后,将控制端置“0”,这样处理会使总线在状态切换时,有一个稳定的工作过程。代码中延迟10ms(参考:https://blog.csdn.net/yx_l128125/article/details/7914102)

#define RS485_TX_EN  PAout(15)   设置RS485 mode控制, RX:0, TX:1void USART_DMA_SendStart(DMA_Channel_TypeDef *DMA_Streamx, u16 m_u16SendCnt)  {    	USART_DMACmd(UART4, USART_DMAReq_Tx, ENABLE);    RS485_TX_EN = 1;	delay_ms(10);	//延迟    DMA_Cmd(DMA_Streamx, DISABLE);   	delay_ms(10);	//延迟	    DMA_SetCurrDataCounter(DMA_Streamx, m_u16SendCnt);      DMA_Cmd(DMA_Streamx, ENABLE);                      	while(1)	{		if(DMA_GetFlagStatus(DMA2_FLAG_TC5)!=RESET)//µÈ´ýͨµÀ5´«ÊäÍê³É		{			DMA_ClearFlag(DMA2_FLAG_TC5);//Çå³ýͨµÀ5´«ÊäÍê³É±êÖ¾			break; 		 }	}					delay_ms(10);	//延迟	RS485_TX_EN=0;   } 

4.5 主函数

int main(void){	    unsigned  char os_err;		OSInit();      //硬件初始化    BSP_Init();         // FIFO 环型处理数据初始化	pfifo = &fifo;	FIFO_Init(pfifo, aRxFIFOBuffer, sizeof(uint8_t), RXFIFOBUFFERSIZE);	    OSInit();	    //先发送一段数据,可屏蔽	for(i = 0; i < 50; i++)	{		G_u8Usart1SendBuf[i] = 0x10 + i;	}	USART_DMA_SendStart(DMA2_Channel5, 50);		 os_err = OSTaskCreate((void (*) (void *)) App_TaskStart, (void *) 0,                    //指向任务代码的指针                         (void *) 0,                                           //任务开始执行时,传递给任务的参数的指针               (OS_STK *) &App_TaskStartStk[APP_TASK_START_STK_SIZE - 1],    //分配给任务的堆栈的栈顶指针   从顶向下递减               (INT8U) APP_TASK_START_PRIO);    //分配给任务的优先级 	    OSTimeSet(0);    OSStart();      /* Start multitasking*/}

五、运行串口调试

 参考:[stm32][ucos] 1、基于ucos操作系统的LED闪烁、串口通信简单例程 - beautifulzzzz - 博客园

stm32f0串口 DMA 空闲中断接收——基于HAL库(代码篇)_噗噗bug博客-CSDN博客

代码:

https://download.csdn.net/download/qq_41070511/24419255

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121077.html

相关文章

  • STM32】标准库与HAL库对照学习教程八--串口通信详解

    摘要:异步通信与同步通信异步通信异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。同步通信同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。配置串口设置为异步通信基础参数波特率为。 ...

    yck 评论0 收藏0
  • 关于STM32 RS485控制I/O口不能正常输出高低电平解决方法

    摘要:当单片机要接收数据的时候,控制为低电平,数据通过接收回来。检测通过万用表测量控制的引脚一直处于高电平,即使函数就单独写将该引脚为低电平,测量出来还是高电平。 一、问题: 问题现象:在进行RS485操作时,发现接收时而进时而不进中断: 将485的AB输出脚直接与串口的TX,RX对接发现串...

    null1145 评论0 收藏0
  • 基于STM32移植UCGUI图形界面框架(3.9.0源码版本)

    摘要:基于的移植教程可以看这里二介绍是一种用于嵌入式应用的图形支持软件。适用于使用任何控制和的任何尺寸的物理和虚拟显示。一个层,称作驱动程序,包含了对的全部访问。并在主函数里加入下面的代码,测试移植是否成功。 一、环境介绍 keil:    5.25 MCU:  STM32F103ZET6 UCG...

    microcosm1994 评论0 收藏0
  • EC20模组使用MQTT库对接EMQX,基于STM32F407

    摘要:一说明本库基于编译,其他的内核也支持,采用串口和模组通信。使用时添加文件到工程中,头文件引用即可。此外,需要外部实现模组的复位操作,一般是对管脚拉高一段时间,复位函数需要指定函数为。四实例以下为使用的库,实现对接自建。 一、说明 本lib库基于STM32F407编译,其他的cortexM4内...

    whatsns 评论0 收藏0
  • STM32F103基于spi实现OLED显示

    摘要:文章目录一原理二实现显示中文滚动显示显示字符串读取温湿度显示温湿度三结果开机显示欢迎信息循环读取温湿度以及滚动显示我的四总结五参考六源码一原理的定义就是串行外围设备接口。 ...

    xfee 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<