1.485底层协议辨识
黄色是PIC发出的,打头的字符是"\r\n\r\n"。绿色的是上位机485接口设备发送字符串“App1"的波形。波特率均为115200。上位机绿色的波形自发自收没有问题。
示波器设置为下降沿触发——485平时空闲状态,线路维持高电平,一旦开始传输,先拉低,然后在传输。串口传输参数是115200,n,8,1
先看下方绿色的数字解码,我们知道他是正确的,二进制码流为:
0-1-0-0-0-0-0-1-0-1-0-0
App的 A的ASCII码为:0x41 = 01000001
绿色的编码似乎是在逆序输出,对吧?把绿色的编码继续分析,找到字符间的分割处:
0-||1-0-0-0-0-0-1-0||-1-0-|| 0-0-0-0-1-1-1-0||-1-0-|| 0-0-0-0-1-1-1-0
注意:p(ASCII码0x70 ,01110000)连发了两次,它的波形是清晰可辨的.
所以,485的发送规则是:平时高电平,发送时,先发送0起始位一个,1停止位一个,连续发送时,以起始位0打头,紧跟字符MSB的8个bits,中间用一个停止位1和一个起始位0隔开。然后是下一个字符的数据区。下面对它做了标注:
按照这个规则,可以看到,PIC一侧发送时,起始位和停止位与平台一侧一致。而第2~第9Bits应该是A,看它发送了什么:这是0d,0a。
2.PIC 485半双工激活
明确这个要点:485半双工的状态切换,仅仅涉及你是不是去发起读操作这一件事。 管教定义此时完全由已经激活的eusart模块接管了。Rx的动作其实一直在进行,它在检测到口线上出现哪怕是自发自收的信号,也会不断触起中断,关键是理不理它。
TXEN、RXEN不需要切换,因为相应的两根PIN管脚始终处于工作状态。GPIO的IN|OUT状态,我们也不需要控制,仅仅在最早的时候,我们需要把相关的Tx,Rx置1,标志由eusart模块接管了这些口线,相应的模拟量标志也需要清理掉。此时口线是 digital状态。
我们当前是去TX\RX管脚读还是写由谁来控制呢?
- 对发送而言,仅仅是对TXREG1的写入动作。上一次写入操作完毕,eusart模块会触发状态通知我们。
- 对接收而言,仅仅是等待485 Recv Char的中断,只要它收到了数,它就会通知我们,缓冲区溢出,帧错误的处理,MCC处理的很好。
但是,我们在Tx发送完毕后,什么时候再重新打开接收中断呢?需要定时器吗?
不需要。关闭Rx的时机,最佳的位置,就是发起写操作的时候,写完,发送完毕,其实发送中断也会有报告,此时:
PIE1bits.TX1IE = 0;
Rs485_ToRecv();
即可,而ToRecv()以及相对称的ToTransmit()函数,只是去切换一下读中断开关:
void Rs485_ToRecv(void)
{
PIE1bits.RC1IE = 1;
}
void Rs485_ToTransmit(void)
{
PIE1bits.RC1IE = 0;
}
在调整参数的时候,MCC的配置会冲毁你之前对mcc自动生成的代码的修改,我这段时间添加了一个侦测函数,来检查MCC是否覆盖了我们改写的代码:
void Peripheral_Params_Check(void)
{
if(!Rs485_IsRegisterSetOK())
{
PromptProgramCrash();
}
else
{
Rs485_ToRecv();
}
}
void PromptProgramCrash(void)
{
char dumb[11];
if(gErrorCode == 0)
{
gErrorCode=0xcccc;
}
xlog("\r\nsys crashed!ErrorCode=%04x", gErrorCode);
sprintf(dumb, "C%04x", gErrorCode);
dumb[9] = '.';
dumb[8] = dumb[4];
dumb[7] = '.';
dumb[6] = dumb[3];
dumb[5] = '.';
dumb[4] = dumb[2];
dumb[3] = '.';
dumb[2] = dumb[1];
dumb[1] = '.';
ShowLED((const char *)(&dumb[0]));
SetLedBlink(false);
while(1)
{
Proc_DealLedRefresh();
}
}
检查到的配置错误状态会写入调试串口,写入LED,还会让故障报警灯闪烁。为了方便故障定位,我定义了一组全局故障码。
把mcc的eusart中断打开,mcc会给你实现一个读写缓冲,并且提供了串口的打印和读取函数(通过stdio.h的那一组标准io函数)。
为了防止后续维护人员不经意间在写入操作未完成之前,发起读485操作,我额外添加了一个防护,确保写入操作完成会,读操作才会启动。
if(!((sizeof(eusart1TxBuffer) == eusart1TxBufferRemaining) && EUSART1_is_tx_done()))
{
return;
}
记得,在发送函数中关闭读中段,在发送中断处理函数中,当发现发送缓冲区,所有字符已经发送完毕,再开读中断。
特别注意:485的阻抗问题,也就是端接电阻,绕不过去,你在搭建回路时,可能需要120欧姆的端接电阻。我的同事给我提供了一个电平转换模块,绕过了阻抗匹配的问题。