返回列表 发新帖我要提问本帖赏金: 100.00元(功能说明)

[应用方案] 485自动收发,为你节省一根IO线

[复制链接]
4039|4
 楼主| Gavin3389 发表于 2021-11-17 15:15 | 显示全部楼层 |阅读模式
本帖最后由 Gavin3389 于 2021-11-16 11:29 编辑

#申请原创#
485自动收发,为你节省一根IO线

@21小跑堂
背景:资源使用过剩,IO引脚紧张。
目的:验证485自动收发电路,确定是否可以节省收发控制脚,满足日常应用。
简述
众所周知,485属于半双工差分通信,传输距离比232远,抗干扰强,且总线可以并联使用。但是,在常规的应用中,mcu端除了收、发数据线外,还需要额外的一个IO引脚,作为收发的方向控制。
原理分析:
10635619319f8885e0.png
首先,RS-485的电气特性:
逻辑‘1’以两线间的电压差为+26V表示;
逻辑‘0’以两线间的电压差为-26V表示。
不管怎么变换,这里的电气特性‘1’‘0’是不会变的。
那么,已此为基准,先从理论上分析下收发状态。

接收时:
默认没有数据时,TX为高电平,三极管导通,RE为低电平使能,
RO收数据有效,MAX485为接收态,正常接收数据。

发送时.
发送“1”时,TX为高电平,三极管导通,DE为低电平,此时收发器处于接收状态,
驱动器就变成了高阻态,也就是发送端与A\B断开了,
此时A\B之间的电压就取决于A\B的上下拉电阻了,A为高电平、B为低电平,
也就成为了逻辑“1”了。
发送“0”时,TX为低电平,三极管截止,DE为高电平,驱动器使能,
此时正好DI是接地的,也就是低电平,
驱动器也就会驱动输出B1A0,也就是所谓的逻辑“00了。

理解自收发的作用,关键是巧妙的使用了收发控制REDE的作用,
尤其是DE0时,驱动器与A\B之间就是高阻态,也就是断开状态,而且A\B都要有上下拉电阻。然后就有了逻辑0-1之间的切换了。所以很巧妙。

然而这里依然需要注意两点:
1、  虽然通过这种形式,可以摆脱“收发控制”,但是对于485来说,依然是“半双工”的通讯模式,如果是全双工,就不行了,因为TX为1时,接收使能,此时从机如果回复数据,那么也就乱了。
2、  TX,RX引脚均需要上拉电阻,这一点特别重要。
开始的时候,所参照的电路就没有上拉电阻,折腾了半天。

~~~~~~~~~~~~~~~~~~~~~~~好了,原理分析结束,开始下一步~~~~~~~~~~~~~~~~~~~~~~~~



电路搭建
找了半天,也没找到合适的PCB,只能委曲求全,在通用板上自己焊接,问题是,手里都是贴装器件,通用板却是插装通用板,无奈,只好开启对付模式,勉勉强强的算是把电路焊接完成,好在电路简单,基本上按照原理图的方向和顺序进行焊接。

TTL端:左侧2.54单排插针,自上而下定义为VDD3V3\RX \TX \GND
485端:右侧绿端子,自上而下定义为B \A
8097361931a2511697.png
怎么测试呢?手里有个之前新唐的开发板M471KIV1.1
TTL端连接开发板,485端连接PC通过串口助手显示数据。
8166761931a4572bb7.png
这里测试的时候,
本来想直接使用例程代码的,通过串口0进行收发测试,但是不知怎的了,就是不能进入接收中断,这个例程明明跑过的,现在却不通了。
然后,改变了下策略,使用串口0的发送,串口1的接收,
将串口1的接收数据通过串口0发送出来,这要注意的是,保证两个串口的波特率一致。

初始化代码如下:
  1. /*----------------------------------------------------------------------*/
  2. /* Init UART0                                                           */
  3. /*----------------------------------------------------------------------*/
  4. void UART0_Init(void)
  5. {
  6.            SYS_UnlockReg();        
  7.         /* Enable UART clock */
  8.     CLK_EnableModuleClock(UART0_MODULE);
  9.     /* Select UART clock source from HIRC */
  10.     CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));        
  11.         /*----------------------------------------------------------------------*/
  12.     /* Init I/O Multi-function                                              */
  13.     /*----------------------------------------------------------------------*/
  14.     /* Set GPB multi-function pins for UART0 RXD and TXD */
  15.     SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk)) |
  16.                     (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);
  17.         
  18.            SYS_LockReg();
  19.         
  20.         SYS_ResetModule(UART0_RST); // Reset UART0
  21.         UART_Open(UART0, 9600);   //Configure UART0 and set UART0 baud rate        
  22.         /* Enable UART RDA and THRE interrupt */
  23.     NVIC_EnableIRQ(UART0_IRQn);
  24.     UART_EnableInt(UART0, UART_INTEN_RDAIEN_Msk);
  25. //    UART_EnableInt(UART0, (UART_INTEN_RDAIEN_Msk | UART_INTEN_THREIEN_Msk));
  26.         
  27. }


  28. /*----------------------------------------------------------------------*/
  29. /* Init UART1                                                          */
  30. /*----------------------------------------------------------------------*/
  31. void UART1_Init(void)
  32. {            
  33.         SYS_UnlockReg();        

  34.     CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART1SEL_HIRC, CLK_CLKDIV0_UART1(1));
  35.     CLK_EnableModuleClock(UART1_MODULE);
  36.         
  37.     SYS->GPB_MFPL = (SYS->GPB_MFPL & ~(SYS_GPB_MFPL_PB2MFP_Msk | SYS_GPB_MFPL_PB3MFP_Msk)) |
  38.                     (SYS_GPB_MFPL_PB2MFP_UART1_RXD | SYS_GPB_MFPL_PB3MFP_UART1_TXD);
  39.         SYS_LockReg();
  40.         
  41.         SYS_ResetModule(UART1_RST);         
  42.     UART_Open(UART1, 9600);

  43.     NVIC_EnableIRQ(UART1_IRQn);
  44.     UART_EnableInt(UART1,UART_INTEN_RDAIEN_Msk);        
  45.         
  46. }
中断处理:
  1. /*---------------------------------------------------------------------------------------------------------*/
  2. /* ISR to handle UART Channel 0 interrupt event                                                            */
  3. /*---------------------------------------------------------------------------------------------------------*/
  4. void UART0_IRQHandler(void)
  5. {
  6.   if (UART_GET_INT_FLAG(UART0,UART_INTSTS_RDAINT_Msk))
  7.      {
  8.          while(!UART_GET_RX_EMPTY(UART0))
  9.              g_u8RecData[g_u32comRhead++] = UART_READ(UART0);
  10.                  UART_Write(UART0, g_u8RecData, g_u32comRhead);               
  11.      }
  12.         g_u32comRhead = 0;
  13.         
  14. }
  15. void UART1_IRQHandler(void)
  16. {
  17.         if (UART_GET_INT_FLAG(UART1,UART_INTSTS_RDAINT_Msk))
  18.                  {
  19.                          while(!UART_GET_RX_EMPTY(UART1))
  20.                          {
  21.                                 UART1_RX_BUF[USART1_RX_STA] = UART_READ(UART1);        
  22.                                 USART1_RX_STA++;
  23.                         //        UART_ClearIntFlag(UART1, UART_INTSTS_RDAINT_Msk);
  24.                          }                 
  25.                  }
  26.         if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收        
  27.                  
  28.         UART_Write(UART0,UART1_RX_BUF,1);
  29.                  USART1_RX_STA=0;
  30.                  
  31.         UART_ClearIntFlag(UART1, (UART_INTSTS_RLSINT_Msk| UART_INTSTS_BUFERRINT_Msk));         
  32. //    if(UART1->FIFOSTS & (UART_FIFOSTS_BIF_Msk | UART_FIFOSTS_FEF_Msk | UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk))
  33. //    {
  34. //        UART_ClearIntFlag(UART1, (UART_INTSTS_RLSINT_Msk| UART_INTSTS_BUFERRINT_Msk));
  35. //    }
  36.                  
  37. }

主函数,这里其实没什么了,还是贴一下吧
  1. int32_t main(void)
  2. {

  3.     /* Init System, IP clock and multi-function I/O. */
  4.     SYS_Init();

  5.     UART0_Init();        
  6.         UART1_Init();
  7. //        UART2_Init();
  8.         
  9.     printf("\n\nCPU [url=home.php?mod=space&uid=72445]@[/url] %dHz\n", SystemCoreClock);

  10.     printf("+-------------------------------------------------+\n");
  11.     printf("|    M471KI8AE Study test     |\n");
  12.     printf("+-------------------------------------------------+\n\n");

  13.         
  14. //        GY39_Init();        
  15. //        
  16. //        GY39_flag=0;GY39_num=0;
  17. //        
  18.         GPIO_Init();
  19.         
  20. //        UART_Write(UART1,CSData,10);
  21.         delay_ms(500);
  22. //        ASK_GY39_T_Pa_RH_He();

  23.     while(1)
  24.         {
  25. //         delay_ms(500);
  26. //     UART_Write(UART0,UART1_RX_BUF,10);
  27.                
  28.         };
  29. }



这里是9600波特率的截屏:

UART1.gif

后面又测试了其它几组波特率,发现在波特率过高的时候,会有乱码出现。
现在不确定是面包板飞线的原因,还是三极管8050的原因。
手里没有其他三极管了,所以,这里暂时没有验证。

结论:
通过上述电路搭建和测试过程,可以看出。在一定的波特率下,采用这种电路形式是可以实现485的自动收发的,从而达到节省控制io引脚的目的。
有兴趣的朋友,可以亲自验证,尝试用到自己的电路中。


后记:

说道这里,网上还有一种电路,电路如下,这也是最开始看到,咋一看好像差别不大,当时就拿来尝试,结果,半天也不通,这才静下心来,一步步的分析电平变化,觉得有问题,又回去百度,才确定了上述的电路原理。而这里其实最关键的就是,txrx没有上拉电阻。

5055561931a974c290.png

网上这个电路的博客,帖子其实还是很多的,也许他们的测试条件不一样,或许MCU端默认加了上拉,但是作为原理验证和测试,单纯的按照这个电路,我是没有成功的,这里拿出来说一下,只是单纯的强调下上拉电阻的重要性。

至此,

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~()~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  

打赏榜单

21小跑堂 打赏了 100.00 元 2021-11-18
理由:恭喜通过原创文章审核!请多多加油哦!

weiwei4dk 发表于 2021-11-19 15:03 | 显示全部楼层
这个电路在实际应用中已经用过很多了
七毛钱 发表于 2021-11-19 17:06 来自手机 | 显示全部楼层
分析的不错
oufuqiang 发表于 2021-11-19 20:00 来自手机 | 显示全部楼层
见一次打一次,自动流控。
QuakeGod 发表于 2021-11-20 10:41 | 显示全部楼层
实在是不怎么样。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

536

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部