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

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

[复制链接]
1544|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发送出来,这要注意的是,保证两个串口的波特率一致。

初始化代码如下:
/*----------------------------------------------------------------------*/
/* Init UART0                                                           */
/*----------------------------------------------------------------------*/
void UART0_Init(void)
{
           SYS_UnlockReg();        
        /* Enable UART clock */
    CLK_EnableModuleClock(UART0_MODULE);
    /* Select UART clock source from HIRC */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));        
        /*----------------------------------------------------------------------*/
    /* Init I/O Multi-function                                              */
    /*----------------------------------------------------------------------*/
    /* Set GPB multi-function pins for UART0 RXD and TXD */
    SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk)) |
                    (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);
        
           SYS_LockReg();
        
        SYS_ResetModule(UART0_RST); // Reset UART0
        UART_Open(UART0, 9600);   //Configure UART0 and set UART0 baud rate        
        /* Enable UART RDA and THRE interrupt */
    NVIC_EnableIRQ(UART0_IRQn);
    UART_EnableInt(UART0, UART_INTEN_RDAIEN_Msk);
//    UART_EnableInt(UART0, (UART_INTEN_RDAIEN_Msk | UART_INTEN_THREIEN_Msk));
        
}


/*----------------------------------------------------------------------*/
/* Init UART1                                                          */
/*----------------------------------------------------------------------*/
void UART1_Init(void)
{            
        SYS_UnlockReg();        

    CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART1SEL_HIRC, CLK_CLKDIV0_UART1(1));
    CLK_EnableModuleClock(UART1_MODULE);
        
    SYS->GPB_MFPL = (SYS->GPB_MFPL & ~(SYS_GPB_MFPL_PB2MFP_Msk | SYS_GPB_MFPL_PB3MFP_Msk)) |
                    (SYS_GPB_MFPL_PB2MFP_UART1_RXD | SYS_GPB_MFPL_PB3MFP_UART1_TXD);
        SYS_LockReg();
        
        SYS_ResetModule(UART1_RST);         
    UART_Open(UART1, 9600);

    NVIC_EnableIRQ(UART1_IRQn);
    UART_EnableInt(UART1,UART_INTEN_RDAIEN_Msk);        
        
}
中断处理:
/*---------------------------------------------------------------------------------------------------------*/
/* ISR to handle UART Channel 0 interrupt event                                                            */
/*---------------------------------------------------------------------------------------------------------*/
void UART0_IRQHandler(void)
{
  if (UART_GET_INT_FLAG(UART0,UART_INTSTS_RDAINT_Msk))
     {
         while(!UART_GET_RX_EMPTY(UART0))
             g_u8RecData[g_u32comRhead++] = UART_READ(UART0);
                 UART_Write(UART0, g_u8RecData, g_u32comRhead);               
     }
        g_u32comRhead = 0;
        
}
void UART1_IRQHandler(void)
{
        if (UART_GET_INT_FLAG(UART1,UART_INTSTS_RDAINT_Msk))
                 {
                         while(!UART_GET_RX_EMPTY(UART1))
                         {
                                UART1_RX_BUF[USART1_RX_STA] = UART_READ(UART1);        
                                USART1_RX_STA++;
                        //        UART_ClearIntFlag(UART1, UART_INTSTS_RDAINT_Msk);
                         }                 
                 }
        if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收        
                 
        UART_Write(UART0,UART1_RX_BUF,1);
                 USART1_RX_STA=0;
                 
        UART_ClearIntFlag(UART1, (UART_INTSTS_RLSINT_Msk| UART_INTSTS_BUFERRINT_Msk));         
//    if(UART1->FIFOSTS & (UART_FIFOSTS_BIF_Msk | UART_FIFOSTS_FEF_Msk | UART_FIFOSTS_PEF_Msk | UART_FIFOSTS_RXOVIF_Msk))
//    {
//        UART_ClearIntFlag(UART1, (UART_INTSTS_RLSINT_Msk| UART_INTSTS_BUFERRINT_Msk));
//    }
                 
}

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

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

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

    printf("+-------------------------------------------------+\n");
    printf("|    M471KI8AE Study test     |\n");
    printf("+-------------------------------------------------+\n\n");

        
//        GY39_Init();        
//        
//        GY39_flag=0;GY39_num=0;
//        
        GPIO_Init();
        
//        UART_Write(UART1,CSData,10);
        delay_ms(500);
//        ASK_GY39_T_Pa_RH_He();

    while(1)
        {
//         delay_ms(500);
//     UART_Write(UART0,UART1_RX_BUF,10);
               
        };
}



这里是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 | 显示全部楼层
实在是不怎么样。

使用特权

评论回复
返回列表 发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则