我也遇到CAN发送问题了.使用的是MC9S12XS64芯片.

2019-07-15 17:28发布

我也遇到 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 completion
       
        return(0);
}

调用函数, 好像发送几次, 把 CAN的发送缓存写完后, 就一直在
do
        {        // 寻找空闲的缓冲器
                CAN0TBSEL = CAN0TFLG;
                send_buf  = CAN0TBSEL;

                DriverWatchdog_feed();//喂看门狗
        }while(!send_buf);

里面循环.
不知道是什么原因. 请指导一下.
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
2条回答
zhu1982lin
2019-07-15 17:37
本帖最后由 zhu1982lin 于 2016-11-1 11:26 编辑

我把 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.
这次遇到问题, 才知道知识共享的重要性.   
还是要大家一起讨论,解决问题.
不好意思, 上面的代码有点问题. 发送时 不能在 死循环, 可以直接将 循环去掉.

一周热门 更多>