This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

清华大学TM4C123微处理器原理与实践例程-CAN实验程序解析

// CAN实验程序解析
//头文件
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_can.h"
#include "inc/hw_ints.h"
#include "driverlib/can.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "utils/uartstdio.h"
#include "driverlib/rom.h"
#include "driverlib/fpu.h"
	
// 变量定义部分
// 一个计数器,用于记录所发送的数据包数
volatile ui32 g_ulMsgCount_TX = 0;
// 一个计数器,用于记录所接收的数据包数
volatile ui32 g_ulMsgCount_RX = 0; 
// 用于指示在传输过程中是否出现错误以及错误类型
volatile ui32 g_bErrFlag = 0; 
//用于指示是否接收到数据包
volatile ui32 g_bRXFlag = 0; 

// 函数定义部分
// UART0的配置及初始化部分。*
// UART0模块用于通过计算机虚拟串口显示过程信息,主要包括InitConsole
//函数和一些UARTprintf语句。
void InitConsole(void)
{
    // 由于UART0使用PA0,PA1两个引脚,因此需要使能GPIOA模块
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	// 将PA0和PA1两个引脚的功能选择为执行UART0模块的功能
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);

    // 对PA0和PA1两个引脚配置为UART功能
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    // UART的标准初始化
    UARTStdioConfig(0,115200,50000000);
}

// 延时1s的函数
Void SimpleDelay(void)
{
	//由于ROM_SysCtlDelay函数延时3个时钟周期,而ROM_SysCtlClockGet
//函数返回系统时钟的频率,因此最终的结果就是延时1s
    ROM_SysCtlDelay(ROM_SysCtlClockGet()/3);
}

// CAN的中断函数 
// 该函数寻找产生中断的原因,并且计算已发送/接收的数据包数目。
//由于要使用CAN0模块的中断函数,所以需要在startup_ccs.c文件的
//vector table(中断函数列表)中声明CANIntHandler。
void CANIntHandler(void)
{
    ui32 ulStatus;

    // 通过CANIntStatus函数读取中断的状态
    ulStatus = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);

    // 如果是控制器状态中断,则说明出现了某种错误
    if(ulStatus == CAN_INT_INTID_STATUS)
    {
		//读取CAN模块所处的状态,并自动清除中断。
        ulStatus = CANStatusGet(CAN0_BASE, CAN_STS_CONTROL);
       //将g_bErrFlag这个状态指示变量置1,以指示有错误发生
        g_bErrFlag = 1;
    }
    // 检查是否是由message object 1引起的发送中断
    else if(ulStatus == 1)
    {
        // 数据包发送已经完成,清除中断
        CANIntClear(CAN0_BASE, 1);

		 //发送完一个数据包,计数器g_ulMsgCount_TX增加
        g_ulMsgCount_TX++;
        // 发送已经完成,清除所有的错误信息.
        g_bErrFlag = 0;
    }

    // 检查是否是由于message object 2引起的接收中断
    else if(ulStatus==2)
    {
        // 数据包接收已经完成,清除中断
        CANIntClear(CAN0_BASE, 2);
		// 接收完一个数据包,计数器g_ulMsgCount_RX增加
		g_ulMsgCount_RX++;

		// 设置Flag说明接收到的数据包正在等待处理
        g_bRXFlag = 1;
        // 发送已经完成,清除所有的错误信息.
		g_bErrFlag = 0;
    }
}

// main 函数
// 配置CAN模块,循环发送CAN格式数据包并通过LOOPBACK模式接收。
// 运行过程中的信息通过UART向计算机机传输。
int main(void)
{
	//使能FPU
	 FPUEnable();
    FPULazyStackingEnable();

    // 定义CAN的发送和接收对象
    tCANMsgObject sCANMessage;
    tCANMsgObject srCANMessage;

    // 定义发送数据存储区和接收数据存储区
    unsigned int uIdx;//一个循环语句使用的变量
    ui8 ucMsgData[4];
    ui8 ucrMsgData[8];

    // 禁用中断。在进行中断配置时要保证中断没被使用。
    IntMasterDisable();

    // 设置系统时钟为50MHz
    SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);

    // 由于CAN0使用PN0、PN1两个引脚,需要使能GPION对应的时钟
//信号
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);

    // 使能CAN0模块的时钟信号
    SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);

    //将PN0和PN1两个引脚的功能选择为执行CAN0模块的功能
    GPIOPinConfigure(GPIO_PN0_CAN0RX);
    GPIOPinConfigure(GPIO_PN1_CAN0TX);

    // 对PN0和PN1两个引脚做有关CAN功能的配置
    GPIOPinTypeCAN(GPIO_PORTN_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    // CAN控制器的初始化
    CANInit(CAN0_BASE);
    // 利用CANBitRateSet函数将CAN的传输速率设置为1MHz
    CANBitRateSet(CAN0_BASE, SysCtlClockGet(), 1000000);

    // 寄存器操作,将CAN0模块配置为运行于LOOPBACK模式
    HWREG(CAN0_BASE + CAN_O_CTL) = HWREG(CAN0_BASE + CAN_O_CTL) | CAN_CTL_TEST;
    HWREG(CAN0_BASE + CAN_O_TST) = HWREG(CAN0_BASE + CAN_O_TST) | CAN_TST_LBACK; 

    // 使能CAN0模块
    CANEnable(CAN0_BASE);
    
    // 使能CAN0模块的中断
    IntEnable(INT_CAN0);
    
    //设置可以引起CAN中断的中断源
    CANIntEnable(CAN0_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    //初始化配置message object,即待发送的报文对象
//配置好报文对象后调用CANMessageSet函数进行设置,
//这样报文就可以自动被发送出去

// 待发送的数据为0
    *(ui32 *)ucMsgData = 0;  
    // CAN message ID:使用1作为报文的ID
    sCANMessage.ui32MsgID = 1;          
// 没有MASK屏蔽
sCANMessage.ui32MsgIDMask = 0;                    
//使能发送中断
sCANMessage.ui32Flags = MSG_OBJ_TX_INT_ENABLE; 
//发送数据包大小为4个字节
sCANMessage.ui32MsgLen = sizeof(ucMsgData);   
// 指向发送数据的指针
sCANMessage.pucMsgData = ucMsgData;          
    
    // 初始化配置用于接收的报文对象
// 同样也是用CANMessageSet函数可以将某个message object(报文对象)
//设置为如下的配置
   // 可以接收任何ID的报文对象
    srCANMessage.ui32MsgID = 0;                 
// 没有屏蔽
srCANMessage.ui32MsgIDMask = 0;             
// 使能接收中断和ID过滤
         srCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE|MSG_OBJ_USE_ID_FILTER;   
// 允许最多8个字节的数据
srCANMessage.ui32MsgLen = 8;       		

    //将message object 2设置为接收报文对象
ROM_CANMessageSet(CAN0_BASE,2,&srCANMessage,MSG_OBJ_TYPE_RX);

    // 使能UART模块
    InitConsole();
    //使能中断
    IntMasterEnable();

    // 开始进入发送数据包的循环, 每秒将发送一个数据包。
    while(1)
    {
        // 将待发送的数据包内容通过UART传输并显示出来
        UARTprintf("Sending msg: 0x%02X %02X %02X %02X",
             ucMsgData[0], ucMsgData[1], ucMsgData[2], ucMsgData[3]);

       // 将待发送的报文配置到message object 1中。
        CANMessageSet(CAN0_BASE, 1, &sCANMessage, MSG_OBJ_TYPE_TX);

        // 等待1s
        SimpleDelay();
        // 通过g_bErrFlag变量查看是否出错
        if(g_bErrFlag)
        {
            UARTprintf(" error - cable connected?\n");
        }
        else
        {
            // 如果没有出错,则显示已经发送的报文数目
            UARTprintf(" total count Transmit = %u\n", g_ulMsgCount_TX);
        }
        // 每次发送完毕,都将报文中的数据内容+1
        (*(ui32 *)ucMsgData)++;

        // 接收报文程序
        // 通过g_bRXFlag判断是否有已经接收到是数据包
        if(g_bRXFlag)        
        {
        	// 建立指向报文数据的缓存区
        	srCANMessage.pucMsgData = ucrMsgData;

        	// 读取接收到的报文对象。
        	// 将message object中的信息读取到srCANMessage接收对象中
        	CANMessageGet(CAN0_BASE,2,&srCANMessage,0);

        	// 将g_bRXFlag置为0。
          //等到下个报文到来时,中断函数会再次将它置1的
        	g_bRXFlag=0;
        	// 如果出现数据丢失等错误,则输出提示信息
        	if(srCANMessage.ui32Flags & MSG_OBJ_DATA_LOST)
        	{
        		UARTprintf("CAN message loss detected\n");
        	}

        	// 通过UART输出接收到的报文信息
        	UARTprintf("Receive Msg ID=0x%08X len=%u data=0x",srCANMessage.ui32MsgID,srCANMessage.ui32MsgLen);

        	// 输出报文中的数据内容
        	for(uIdx=0;uIdx<srCANMessage.ui32MsgLen;uIdx++)
        	{
        		UARTprintf("%02X",ucrMsgData[uIdx]);
        	}

        	// 输出所有已收到的报文数目
        	UARTprintf(" total count Received = %u\n", g_ulMsgCount_RX);
        }
    }
    return(0);
}
  • // 寄存器操作,将CAN0模块配置为运行于LOOPBACK模式
     HWREG(CAN0_BASE + CAN_O_CTL) = HWREG(CAN0_BASE + CAN_O_CTL) | CAN_CTL_TEST;
     HWREG(CAN0_BASE + CAN_O_TST) = HWREG(CAN0_BASE + CAN_O_TST) | CAN_TST_LBACK; 
    将以上两行注释后就不能发出报文了(当然也就谈不上接收发出去的报文了),经测试,注释后进不了中断服务程序。请教您一下,多谢。
  • 楼主的问题解决了没有,有关我的回复,在另一个帖子中有说明,如果您已经解决了问题,希望您能够分享经验。