|
本帖最后由 yxydoctor 于 2022-5-27 13:24 编辑
RTX51 Tiny是面向51核编程的一款RTOS,因为51核很成熟也很固定,所以移植该RTOS没有难度,基本算白捡。
T5L提供了高达206MHz的51主频,虽然算力测试跟32位机比还差点,但在现场数据采集,通讯,显示这块倒是强项。
结合RTX51 Tiny和T5L的C51编程,给出一种CAN接口通讯的简单收发流程,仅供参考。
一 RTX51 Tiny移植注意事项
1 - OS时钟滴答设为1ms,移植模板为10ms是因为标准51主频12M,Timer0滴答只有1MHz
2 - 关闭任务轮询设置(关闭时间片),即何时退出任务需要用户人工自行设定。这样做的好处是规避不同任务同时访问
同一软件代码或同一硬件外设。
3 - 关闭休眠模式,因为T5L没有休眠模式,传统51的休眠寄存器已挪作它用了。
二 CAN通讯流程简介
1 - CAN中断函数仅负责置标志位,或者置OS信号量;中断函数里面不要尝试读写CAN报文;
2 - CAN接收任务:由中断函数进行触发,达到快速响应的目的;
3 - CAN发送任务:以1ms间隔查询发送报文队列,按需发送单帧或多帧;因为1ms延迟对发送影响不大,所以采用查询方式。
三 CAN中断函数
/***********************************************************************************************
功 能:CAN中断函数
说 明:包括发送,接收,故障中断
***********************************************************************************************/
void can_isr() interrupt 9
{
// 远程帧
if((CAN_IR&0xC0) == 0xC0)
{
CAN_IR &= 0x3F; // 清空远程帧标记位
}
// 标准帧
else if((CAN_IR&0xC0) == 0x40)
{
CAN_IR &= 0xBF; // 清空数据帧标记位
CAN0.CommFailCount=0; // 置接收完成标记
isr_send_signal(JOB_CANRX); // RTX51通知事件(rx完成)
}
// 发送成功
if((CAN_IR&0x20) == 0x20)
{
CAN_IR &= 0xDF; // 清空发送帧标记位
if(tx_message_id < 32 && CAN0.Tx_Count[tx_message_id] > 0)
CAN0.Tx_Count[tx_message_id] --;
tx_message_id = CAN_TX_IDLE;
isr_send_signal(JOB_CANTX); // RTX51通知事件(tx完成)
}
if((CAN_IR&0x10) == 0x10)
{
CAN_IR &= 0xEF; // 清空接收溢出标记位
}
if((CAN_IR&0x08) == 0x08)
{
CAN_IR &= 0xF7; // 清空错误标记位
CAN_ET &= 0xE0; // 清空错误标记
}
if((CAN_IR&0x04) == 0x04)
{
CAN_IR &= 0xFB; // 清空仲裁失败标记位
CAN_CR |= 0x04; // 重新启动发送
}
}
本例程只涉及到标准帧+数据帧的读写。发送,接收成功后,都要给对应的解析任务提供一个OS信号量作为触发条件。
四 CAN接收任务
/***********************************************************************************************
功 能:读CAN接收报文函数
说 明:放在任务中读,避免在中断中访问DGUS变量,实测<30us
***********************************************************************************************/
void can_rxMessage(void)
{
uint8_t IDE_RTE,DLC,i,mo,d[16];
uint16_t id;
// 1-读出0xFF:0068 -0xFF:006B的CAN相关数据
read_dgus4byte(0xFF0068, d, 4);
// 2-判断数据格式是否正确
IDE_RTE = d[0] >> 6; if(IDE_RTE) return; // 扩展帧bit7(IDE)或远程帧bit6(RTE)
DLC = d[0] & 0x0F; // 帧数据长度bit3..0(DLC)
// 3-判断邮箱编号
id = d[4]<<3; id |= (d[5]>>5); // 报文id号
mo=250;
for(i=0;i<32;i++){
if(DLC != CAN0.Id){mo=i; break;}
}
// 4-拷贝数据,设接收标志
if(CAN0.Rx_Count[mo] < CAN_BUF_LEN){
if(mo < 32 && DLC == CAN0.Size[mo]){
memcpy(CAN0.Rx_Data[mo][CAN0.Rx_Count[mo]], &d[8],8);
CAN0.Rx_Count[mo] ++;
}
}
}
上述代码由接收任务调用,由中断函数触发,为单报文解析读取函数。报文判断合格后,打入缓冲区。
五 CAN发送任务
/***********************************************************************************************
功 能:CAN报文发送函数
说 明:tx成功/超时后再退出
***********************************************************************************************/
void can_txMessage(uint8_t mo, uint8_t index)
{
uint8_t state, d[16]={0};
// 1-准备发送数据
d[0] = CAN0.Size[mo]; // 数据帧+标准格式:[7] IDE ,[6]RTR, [3:0]—DLC,帧数据长度
d[4] = CAN0.Id[mo]>>3; // 报文id号
d[5] = (CAN0.Id[mo] & 0x07)<<5;
memcpy(&d[8], CAN0.Tx_Data[mo][index],8);
write_dgus4byte(0xFF0064, d, 4);
// 2-启动发送
tx_message_id = mo;
CAN_CR |= 0x04;
// 3-发送完毕/超时判断
state = os_wait2 (K_SIG | K_TMO, 5);
// 超时判断:5ms超时,视为总线故障(比如断缆),取消发送
if(state == TMO_EVENT){
CAN0.Tx_Count[tx_message_id] = 0;
tx_message_id = CAN_TX_IDLE;
}
}
发送任务的发送成功与否放在最后判断,分为两种情况:
1-正常发送:因为CAN速度很快,所以发送时间很短;该条件由中断触发;
2-未发送成功:比如总线断缆,此时等待5ms也退出。
这样的话对于每一帧发送,无论成功与否,都会给个结论,如果5ms发送不成功,则取消该邮箱的所有发送任务。
六 小结
1 - 借助于RTX51 Tiny,可以简化T5L单片机的CAN收发流程。
2 - 时间开销主要是CAN寄存器或DGUS地址读写时间,实测<10us,而等待发送成功或超时检测时间不占用CPU。
3 - 收发缓冲区大小取决于xdata容量。比如设置32个邮箱,缓冲区深度为10时的测试代码,xdata=6K,可以放心使用。
4 - 使用RTOS后切记一点,用户代码中不要对EA进行控制,不要对Timer0寄存器进行控制。迪文官方推荐的进入中断关EA策略,其实是不可取的。
七 测试
借助于zlg的USBCAN模块和CAN Test软件做报文收发测试,不断提高发送频率,单次发送报文数量。效果很好。
|
|