一、概念
NXP1778芯片内部自带一个CAN控制器。CAN模块由两部分组成:控制器和验收滤波器。所有的寄存器和RAM都以32位字宽度来访问。
CAN自动波特率检测主要用于以一种CAN的转换的产品中。产品中CAN部分的波特率以前都是通过网页中修改然后配置下去的,依据实际情况的考虑,如果CAN部分的波特率能够根据客户设备的改变而自动改变,将会增加其灵活性。
二、原理
根据CAN控制器的特点,理论上有两种实现的方式。一是,通过在接收端处理波特率协商,另外是通过在发送时处理。理论上这两种方式都可以实现,因为当双方波特率不匹配时,当出现被动错误时就说明数据发送失败,然后根据波特率表切换到下一个波特率继续检测,看是否出现被动错误。
三、流程
根据原理,最开始使用的是在接收端时处理,由于CAN模块的数据接收是以中断控制的,所以波特率协商只能在中断中实现;由于CAN控制器对于没有发送成功的数据会进行重发,所以导致CAN的接收中断触发的时间极短,而在其中处理波特率检测和切换时间开销很大,所以这种方式测试的结果是波特率协商出错;基于这种情况,后面又改进了这种处理方式,就是在定时器中断中处理波特率切换,只在CAN的接收中断中设置标志判断波特率是否正确,经过测试这种方式明显要比改进之前的要好,但由于都是在中断中处理,对于时间的把握不是很好,在某些波特率下还是不能切换。总之,在中断中处理始终不是很好,因为对于时间的把握始终不是很好,所以最终放弃了这种方式。
后面一种方式是在发送端处理,即采用在循环中轮询发送数据以及判断总线是否出错需要波特率的切换。具体的方式是在发送一个数据之后就等待出现被动错误,如果到达一定时间还没有出现就确定发送成功。根据这一原理进行了实现并测试,发现在波特率为80K的情况下出现不能切换的问题,在这一问题上进行了多次调试,发现其实在出现被动错误的时候CAN控制器会重发还多次才能达到,但当波特率设置为80K时,重发过程中都不会引发被动错误,所以根据测试结果,认为判断条件有时候需要提前结束,而不必等到达到错误被动。最后,根据情况在进行切换的条件的总线错误中添加了总线错误中断的判断。
四、部分代码
//判断是否进行波特率切换和超时退出
void set_change_state_erred(uint8 canid)
{
LPC_CAN_TypeDef* pCan;
pCan = CAN_GetPointer(canid);
//主动错误或者总线上检测到错误时,表示数据发送失败
//"主动错误"应定义为:CANxGSR中的ES位为1,并且发送出错计数器>=128
//总线错误定义为:CANxICR中的BEI位为1,即是检测到总线错误中断被置位
if(((pCan->GSR & CAN_GSR_ES)
&&((pCan->GSR & 0xff000000) >=128))
|| (pCan->ICR & CAN_ICR_BEI))
{
//遍历一次波特率表,如果没有协商到说明没有波特率与之匹配
//强制设置波特率为最大值1000K
//同时立即退出波特率协商,不必等待超时
if(try_num[canid] == 16)
{
try_num[canid] = 0;
CAN_setting[canid].baudrate = set_baudrate[try_num[canid]];
auto_csd_start(canid);
baudrate_change_state[canid] = 0;
return ;
}
//波特率相同不必重复检测
if(CAN_setting[canid].baudrate ==set_baudrate[try_num[canid]])
{
try_num[canid]++;
}
//切换到位速率表中下一个
CAN_setting[canid].baudrate = set_baudrate[try_num[canid]];
auto_csd_start(canid);
send_data_state[canid] = 0;
try_num[canid]++;
}
//设置超时时间1s
//如果时间到还没有出现错误,则证明波特率正确,
//则停止本次波特率的协商
else if( baudrate_time_out[canid] >= 1000)
{
baudrate_change_state[canid] = 0;
}
}
//每次发送一帧测试数据
void send_test_data(uint8 canid)
{
if(send_data_state[canid] == 0)
{
my_can_write(&CAN_port[canid],(tCAN_frame *)test_arry,1);
baudrate_time_out[canid] = 1;
send_data_state[canid] = 1;
}
}
//主函数调用
void auto_baudrate_set(void)
{
uint8 i;
for(i=0;i
{
if(baudrate_change_state[i]== 1)
{
send_test_data(i);
set_change_state_erred(i);
}
}
}
//自动波特率检测时,设置波特率初值,与网页配置无关
void auto_baudrate_init(void)
{
uint8 i;
//初始化两路CAN的波特率均为1000K
//这个值与网页配置无关,并不保存在flash中
for(i=0;i
{
CAN_setting[i].baudrate = set_baudrate[0]; //1000K
}
}
void get_pin_state(void)
{
uint8 i;
//获取引脚状态,当为低时,启动波特率协商
if(GPIO_ReadValue(UP_APP_PORT)&UP_APP_MASK)
{
if(pin_state != 0)
{
pin_state = 0;
}
return ;
}
//添加pin_state的状态获得,主要是用于当客户设备的波特率改变时
//可以通过控制引脚的上拉和下拉来随时启动波特率改变
//主要方式为:当客户设备波特率设置好之后,拉低引脚来启动波特率改变
//第一次协商好之后,如果客户设备的波特率改变,则需要将引脚拉高,然后再拉低
//来进行波特率的改变
else
{
if(pin_state == 0)
{
for(i=0;i
{
try_num[i] = 0;
send_data_state[i] = 0;
baudrate_change_state[i] = 1;
auto_csd_start(i);
}
pin_state = 1;
}
else
{
return ;
}
}
}
五、测试情况
目前通过ZLG的CAN-TEST软件和自己的设备测试,都能进行切换。最大支持16个波特率的切换,并且可以实现两路同时自动协商。