我也遇到 CAN的发送问题, 数据发送不出去.
以下是我的代码:
CAN 初始化的函数:
void DriverCan0_init(void)
{
//----------------------------
//设置 CAN引脚
//STB(PA0) 为待机控制输入(低电平有效)
DDRA_DDRA0 = 1; // TJA1040_STB#
PORTA_PA0 = 1; // disable standby of TJA1040
DDRM_DDRM0 = 0;//PTM0 输入
DDRM_DDRM1 = 1;//PTM1 输出
PERM_PERM0 = 1; PPSM_PPSM0 = 0;//PTM0 上拉
WOMM_WOMM1=0;
//PTM_PTM0 = 1; PTM_PTM1 = 1;//在PORT M的数据寄存器里PM0,PM1位写1就可以正常收发了. //需要吗?
//----------------------------
//if(CAN0CTL0_INITRQ==0) { // 查询是否进入初始化状态
CAN0CTL0_INITRQ =1; // 进入初始化状态
//}
while (CAN0CTL1_INITAK==0); //等待进入初始化状态
CAN0BTR0_SJW = 0; //设置同步 (1 Tq时钟周期)
CAN0BTR0_BRP = 1; //设置波特率 (分频数 2)
CAN0BTR1 = 0x1C; //设置时段1和时段2的Tq个数 ,总线频率为500kb/s
// 打开滤波器(CAN0IDAC使用默认值,32位接收滤波)
//标准帧的最高3bit为 (101)2
CAN0IDAC_IDAM = 0x0; // Two 32-bit acceptanc filter
CAN0IDMR0 = 0x1F; CAN0IDMR1 = 0xFF;
CAN0IDMR2 = 0x00; CAN0IDMR3 = 0x00;
CAN0IDMR4 = 0x00; CAN0IDMR5 = 0x08;//完全不接收数据
CAN0IDMR6 = 0x00; CAN0IDMR7 = 0x00;
//---------
CAN0IDAR0 = 0xA0; CAN0IDAR1 = 0x00; //标准帧的最高3bit为 (101)2
CAN0IDAR2 = 0x00; CAN0IDAR3 = 0x00;
CAN0IDAR4 = 0x00; CAN0IDAR5 = 0x00;
CAN0IDAR6 = 0x00; CAN0IDAR7 = 0x00;
//-----------------------------
CAN0CTL1 = 0x80; //使能MSCAN模块,设置为一般运行模式、使用振荡器时钟源
CAN0MISC = 0x01; //模块总线脱离,并保持该状态直到用户请求.
CAN0CTL0 = 0x00; //返回一般模式运行
while(CAN0CTL1_INITAK); //等待回到一般运行模式
while(CAN0CTL0_SYNCH==0); //等待总线时钟同步
CAN0RFLG_RXF = 1; //清除 接收标志
CAN0RIER_RXFIE = 1; //使能接收中断
//=====================================
DDRM_DDRM0 = 0;//PTM0 输入
DDRM_DDRM1 = 1;//PTM1 输出
PERM_PERM0 = 1; PPSM_PPSM0 = 0;//PTM0 上拉
WOMM_WOMM1=0;
//PTM_PTM0 = 1; PTM_PTM1 = 1;//在PORT M的数据寄存器里PM0,PM1位写1就可以正常收发了. //需要吗?
}
CAN 发送的函数:
/**************************************************************
* 名称 : DriverCan0_sendMsg
* 功能 : CAN0 发送数据;
*
* 参数 : msg : in : 要发生的CAN数据;
* 返回 : err : 错误标记; 0 : OK; 1:参数错误; others:其他错误;
*
* 备注 : 1.
**************************************************************/
uint8_t DriverCan0_sendMsg(const CAN_MSG_T *pTxMsg)
{
uint8_t send_buf, sp, len;
len = pTxMsg->len;
if(len > 8) { return(1); }// 检查数据长度
if(CAN0CTL0_SYNCH == 0) { return(2); }// 检查总线时钟
send_buf = 0;
do
{ // 寻找空闲的缓冲器
CAN0TBSEL = CAN0TFLG;
send_buf = CAN0TBSEL;
DriverWatchdog_feed();//喂看门狗
}while(!send_buf);
// 写入标识符
CAN0TXIDR0 = (uint8_t)((pTxMsg->id>>3) & 0xFF);
CAN0TXIDR1 = (uint8_t)((pTxMsg->id<<5) & 0xFF);
// 写入数据
for(sp = 0; sp < len; sp++)
{
*((&CAN0TXDSR0)+sp) = pTxMsg->data[sp];
}
CAN0TXDLR = len; // 写入数据长度
CAN0TFLG = send_buf;// 清 TXx 标志 (缓冲器准备发送)
// while ( (CAN0TFLG&send_buf) != send_buf); // Wait for Transmission comple
tion
return(0);
}
调用函数, 好像发送几次, 把 CAN的发送缓存写完后, 就一直在
do
{ // 寻找空闲的缓冲器
CAN0TBSEL = CAN0TFLG;
send_buf = CAN0TBSEL;
DriverWatchdog_feed();//喂看门狗
}while(!send_buf);
里面循环.
不知道是什么原因. 请指导一下.
我把 CAN调通了. 居然是外部 TJA1040 的 STB管脚控制问题. 我一直把芯片误设置成休眠了.
为了感谢帮我解决问题的网友, 也问题后面遇到问题的人, 我把我写的CAN驱动分享出来.
我用的芯片是 MC9S12XS64. 外部芯片是 TJA1040. PA0 接 TJA1040的STB脚.
/**************************************************************
* 名称 : DriverCan0_init
* 功能 : CAN0 初始化;
*
* 参数 : 无
* 返回 : 无
*
* 备注 : 1.使用振荡器时钟源,振荡器时钟源是 16MHz.
**************************************************************/
void DriverCan0_init(void)
{
//----------------------------
//设置 CAN引脚
//STB(PA0) 为待机控制输入(低电平有效)
DDRA_DDRA0 = 1; // TJA1040_STB#
PORTA_PA0 = 0; //低电平为正常模式, 高电平为休眠状态 // disable standby of TJA1040
DDRM_DDRM0 = 0;//PTM0(Rx) 输入
DDRM_DDRM1 = 1;//PTM1(Tx) 输出
PERM_PERM0 = 1; PPSM_PPSM0 = 0;//PTM0 上拉
//WOMM_WOMM1=0;
//PTM_PTM0 = 1; PTM_PTM1 = 1;//在PORT M的数据寄存器里PM0,PM1位写1就可以正常收发了. //需要吗?
//----------------------------
//if(CAN0CTL0_INITRQ==0) { // 查询是否进入初始化状态
CAN0CTL0_INITRQ =1; // 进入初始化状态
//}
while (CAN0CTL1_INITAK==0); //等待进入初始化状态
CAN0CTL1 = 0x80; //使能MSCAN模块,设置为一般运行模式、使用振荡器时钟源 CAN0CTL1 = 0xA0; //环回自测 //
// CAN0BTR0_SJW = 0; //设置同步 (1 Tq时钟周期)
// CAN0BTR0_BRP = 1; //设置波特率 (分频数 2)
// CAN0BTR1 = 0x1C; //设置时段1和时段2的Tq个数 ,总线频率为500kb/s
//--------------
//为了去除 晶振毛刺
CAN0BTR0_SJW = 3; //设置同步 (4 Tq时钟周期)
CAN0BTR0_BRP = 1; //设置波特率 (分频数 2)
CAN0BTR1 = 0x1C; //设置时段1和时段2的Tq
// 打开滤波器(CAN0IDAC使用默认值,32位接收滤波)
//标准帧的最高3bit为 (101)2
CAN0IDAC_IDAM = 0x1; // 4个16位 acceptanc filter
CAN0IDMR0 = 0x1F; CAN0IDMR1 = 0xFF;
CAN0IDMR2 = 0x00; CAN0IDMR3 = 0x00;//完全不接收数据
CAN0IDMR4 = 0x00; CAN0IDMR5 = 0x00;
CAN0IDMR6 = 0x00; CAN0IDMR7 = 0x00;
//---------
CAN0IDAR0 = 0xA0; CAN0IDAR1 = 0x00; //标准帧的最高3bit为 (101)2
CAN0IDAR2 = 0x00; CAN0IDAR3 = 0x00; //完全不接收数据
CAN0IDAR4 = 0x00; CAN0IDAR5 = 0x00;
CAN0IDAR6 = 0x00; CAN0IDAR7 = 0x00;
//-----------------------------
CAN0MISC = 0x01; //模块总线脱离,并保持该状态直到用户请求.
CAN0CTL0 = 0x00; //返回一般模式运行
while(CAN0CTL1_INITAK); //等待回到一般运行模式
while(CAN0CTL0_SYNCH==0); //等待总线时钟同步
CAN0RFLG_RXF = 1; //清除 接收标志
CAN0RIER_RXFIE = 1; //使能接收中断
//=====================================
DDRM_DDRM0 = 0;//PTM0 输入
DDRM_DDRM1 = 1;//PTM1 输出
PERM_PERM0 = 1; PPSM_PPSM0 = 0;//PTM0 上拉
//PTM_PTM0 = 1; PTM_PTM1 = 1;//在PORT M的数据寄存器里PM0,PM1位写1就可以正常收发了. //需要吗?
}
/**************************************************************
* 名称 : DriverCan0_sendMsg
* 功能 : CAN0 发送数据;
*
* 参数 : msg : in : 要发生的CAN数据;
* 返回 : err : 错误标记; 0 : OK; 1:参数错误; others:其他错误;
*
* 备注 : 1.
**************************************************************/
uint8_t DriverCan0_sendMsg(const CAN_MSG_T *pTxMsg)
{
uint8_t send_buf, sp, len;
len = pTxMsg->len;
if(len > 8) { return(1); }// 检查数据长度
if(CAN0CTL0_SYNCH == 0) { return(2); }// 检查总线时钟
send_buf = 0;
//do //不能死循环, 因为外部CAN没连接的情况下,会一直循环下去.
// { // 寻找空闲的缓冲器
CAN0TBSEL = CAN0TFLG;
send_buf = CAN0TBSEL;
DriverWatchdog_feed();//喂看门狗
//}while(!send_buf);
// 写入标识符
CAN0TXIDR0 = (uint8_t)((pTxMsg->id>>3) & 0xFF);
CAN0TXIDR1 = (uint8_t)((pTxMsg->id<<5) & 0xFF);
// 写入数据
for(sp = 0; sp < len; sp++)
{
*((&CAN0TXDSR0)+sp) = pTxMsg->data[sp];
}
CAN0TXDLR = len; // 写入数据长度
CAN0TFLG = send_buf;// 清 TXx 标志 (缓冲器准备发送)
// while ( (CAN0TFLG&send_buf) != send_buf); // Wait for Transmission completion
return(0);
}
/**************************************************************
* 名称 : DriverCan0_errRest
* 功能 : CAN0 出错复位;
*
* 参数 : 无
* 返回 : 无
*
* 备注 : 1. 出错太多,重新复位.
**************************************************************/
void DriverCan0_errRest(void)
{
if(CAN0RFLG_CSCIF)
{
DriverCan0_init();//重新复位
}
}
/*************************************************************/
/* 中断接收函数 */
/*************************************************************/
#pragma CODE_SEG __NEAR_SEG NON_BANKED
static CAN_MSG_T gCanMsgRx;
interrupt 38 void CAN_receive(void)
{
uint8_t sp2;
if(!(CAN0RFLG_RXF)) { return; }// 检测接收标志
// 检测 CAN协议报文模式 (一般/扩展) 标识符
if(CAN0RXIDR1_IDE) { return; }//不接收 扩展帧
if(CAN0RXIDR1&0x10) { return; }//不接收远程帧 //RTR: 0:数据帧.
// 读标识符
gCanMsgRx.id = (((uint16_t)CAN0RXIDR0)<<3) | (((uint16_t)CAN0RXIDR1)>>5);
// 读取数据长度
gCanMsgRx.len = CAN0RXDLR & 0x0F;
// 读取数据
for(sp2 = 0; sp2 < gCanMsgRx.len; sp2++)
{
gCanMsgRx.data[sp2] = *((&CAN0RXDSR0)+sp2);
}
CAN0_RECE_DATA(&gCanMsgRx);
CAN0RFLG_RXF = 1;// 清 RXF 标志位 (缓冲器准备接收)
}
#pragma CODE_SEG DEFAULT
=====================
其中: CAN0_RECE_DATA(&gCanMsgRx); 是在其他地方实现的一个函数. 举个例子:
#pragma CODE_SEG __NEAR_SEG NON_BANKED
/**************************************************************
* 名称 : CAN0_RECE_DATA
* 功能 : CAN0 中断中接收到数据的处理函数;
*
* 参数 : 无
* 返回 : 无
*
* 备注 : 1.此函数在中断中运行.
**************************************************************/
void CAN0_RECE_DATA(const CAN_MSG_T* tMsg)
{
if(tMsg->id == 0x581)
{
asm NOP; //自己修改 ID值, 添加 处理方式.
} else if(tMsg->id == 0x580) { //校准指地址从板电流,电压,下载电池组单节电池容量
asm NOP; //自己修改 ID值, 添加 处理方式.
}
}
#pragma CODE_SEG DEFAULT
有问题可以加我的QQ: 53827302.
这次遇到问题, 才知道知识共享的重要性.
还是要大家一起讨论,解决问题.
不好意思, 上面的代码有点问题. 发送时 不能在 死循环, 可以直接将 循环去掉.
一周热门 更多>