最近在完成一个modbus网关的项目需要将服务端下发的数据流用485接口和从机进行通讯。
带485接口的物联网网关
作为一枚入行两年的资深小白,开发的第一步当然是打开Demo,拷贝它学习它。
从注释来看样例程序使用的是自动方向控制模式(AUD)。再看一下DataSheet的介绍,我了解到IC的UART控制器本身支持的模式共三种:RS-485 普通多点操作模式(NMM),RS-485 自动地址识别模式(AAD),还有本文将要介绍的自动方向控制模式。
想继续看懂DataSheet中专业的寄存器描述,我们需要先热热身--了解一下什么是485。
485原理图
首先485不是一种软件协议而是一种硬件上的串行通讯标准。
485的电气特性为逻辑"1":+(2v~6v)压差,逻辑"0":-(2v~6v)压差。RS485有两种接线一种是四线制一种是两线制。原因在于485的传输信号为差分信号,即使在某时刻单向传输也需要两个引脚共同作用,想要全双工就得比RS232多两根线用四线制。
除了两个引脚产生差分信号,485通信过程中还需要两个引脚进行流控制。这两个引脚的功能相当于门卫。它们一个负责放行自己人外出,大名CTS(clear to send,发送允许);一个允许别人进入,名号RTS(require to send,发送请求)。CTS是输入信号,CTS检测到有效电平时说明本设备可发送数据出去;RTS为输出信号,RTS输出有效电平来告知其他设备自己可以接受数据。
笔者调试用的485转USB
了解到这里再查看一下手册就可以写代码了,下面贴出我的代码。
首先是初始化:
/* 使能UART_1 时钟*/
CLK_EnableModuleClock(UART1_MODULE);
/* 选择UART_1 时钟源 */
CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART_S_HXT, CLK_CLKDIV_UART(1));
/* 设置UART_1 复用引脚 */
SYS->GPB_MFP &= ~(SYS_GPB_MFP_PB4_Msk | SYS_GPB_MFP_PB5_Msk |
SYS_GPB_MFP_PB6_Msk | SYS_GPB_MFP_PB7_Msk);
SYS->GPB_MFP |= SYS_GPB_MFP_PB4_UART1_RXD | SYS_GPB_MFP_PB5_UART1_TXD |
SYS_GPB_MFP_PB6_UART1_nRTS | SYS_GPB_MFP_PB7_UART1_nCTS;
/设置中断优先级/
NVIC_SetPriority(UART02_IRQn,1);
/复位UART_1/
SYS_ResetModule(UART1_RST);
/开启UART_1/
UART_Open(UART1, u1_baudrate);
/使能UART_1中断/
UART_EnableInt(UART1, (UART_IER_RDA_IEN_Msk | UART_IER_TOUT_IEN_Msk));
/* 选择自动方向控制模式 */
UART_SelectRS485Mode(UART1, UART_ALT_CSR_RS485_AUD_Msk, 1);
/* Set RTS pin active level as high level active */
UART1->MCR &= ~UART_MCR_LEV_RTS_Msk;
UART1->MCR |= UART_RTS_IS_HIGH_LEV_ACTIVE;
/counting clock = baud/
/* Set TX delay time */
UART1->TOR = 0x2000;
/* Set RX delay time */
UART1->TOR |= 0x28;
发送函数:
void RS485_SendDataByte(uint8_t *pu8TxBuf, uint32_t u32WriteBytes)
{
/* Set UART parity as SPACE and skip baud rate setting */
UART_SetLine_Config(UART1, 0, UART_WORD_LEN_8, UART_PARITY_NONE, UART_STOP_BIT_1);
/* Send data */
UART_Write(UART1, pu8TxBuf, u32WriteBytes);
//printf(" ! 485_Snd_over.\r\n");
}
接受中断:
void UART1_IRQHandler()
{
uint8_t u8InChar = 0xFF;
uint32_t u32IntSts = UART1->ISR;
if(u32IntSts & UART_ISR_RDA_INT_Msk)
{
/* Get all the input characters */
while(UART_IS_RX_READY(UART1))
{
/* Get the character from UART Buffer */
u8InChar = UART_READ(UART1);
/* Check if buffer full */
if(u1_rcv_tail < U1_RXBUFSIZE)
{
/* Enqueue the character */
u1_rcv_buf[u1_rcv_tail] = u8InChar;
//printf("_u1_0x%02x\r\n",u8InChar);
u1_rcv_tail = (u1_rcv_tail == (U1_RXBUFSIZE - 1)) ? 0 : (u1_rcv_tail + 1);
}
}
}
}
定时器判断一帧数据结束:
//FrameOver_1 在主轮询中清零
void Uart_1_RxMonitor(void)
{
static uint8_t idletmr = 0;
if(u1_rcv_tail){
if(u1_rcv_tail != old_u1_rcv_tail){
old_u1_rcv_tail = u1_rcv_tail;
idletmr = 0;
}
else if(++idletmr > U1_IDLE_TIM )
FrameOver_1 = 1;
}
}
物联网信息交流群 QQ:985441916