查看: 9594|回复: 4

[分享] [转载]LPC1768 CAN通讯知识点分享

[复制链接]
  • TA的每日心情
    开心
    2025-8-8 16:43
  • 签到天数: 1504 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    97

    主题

    4693

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    10105
    最后登录
    2025-9-11
    发表于 2019-6-26 15:11:57 | 显示全部楼层 |阅读模式
    由上图可见,整个CAN控制器一头是CPU,另一头是CAN收发器:
         CAN收发器负责CAN数据与CAN网络的通信。CAN内核模块解析和封装要发送到CAN收发器以及从CAN
       收发器发过来的数据,此处CAN内核工作由硬件自行完成。
         CPU通过APB总线即可设置CAN控制器状态,以及读取中断信息和中断状态。
         一共有3个发送缓冲器(邮箱),这样就可以保证,最少可以发送3组并发的CAN数据;2个接收缓冲
       器(邮箱),这样就可以在CPU处理1个邮箱的接收数据的同时,还能用另一个邮箱接收网络上的数据。
         LPC1769 CAN的验收滤波器比较特殊,它是一个独立于CAN控制器的器件,也属于一种外设,不过
       比较特殊的是,它是服务于CAN控制器的外设,这么做的意义就在于,验收滤波这方面,不再需要软件来
       来做任何事情,直接由硬件来实现查表算法,节省宝贵的CPU资源,由于它也算是一个独立的外设,运用
       起来也比较复杂,而本篇篇幅有限,暂不详述,下次再另开篇博客来说。
         至此,LPC1769 CAN的结构就介绍完毕,接下说明,做哪些事情可让其开始正确收发CAN数据。
            CAN1/2 使用以下寄存器进行设置:
            a)电源使能:在PCONP 寄存器中,设置PCAN1/2。
            注:复位时,CAN1/2 会被使能(PCAN1/2=0)。
            b)时钟使能:在PCLK_SEL0 寄存器中选择PCLK_CAN1/2 和验收滤波器的PCLK_ACF。
            注:如果所需使用的 CAN 波特率必须高于100kbit/s(参见表16.12),那么就不能选择IRC
         作为时钟源。
            c)唤醒:CAN 控制器能够将微控制器从掉电模式唤醒。
            d)引脚:通过PINSEL 寄存器选择CAN1/2 引脚,并通过PINMODE 寄存器选择引脚模式。
            e)中断:CAN 中断是通过CAN1/2IER 寄存器来使能的。中断的使能是通过在NIVC 中使
            用相应的中断设置使能寄存器(Interrupt Set Enable register)来实现的。
            f)CAN 控制器初始化:在CANNOD 寄存器中设置。

            以上为数据手册介绍的CAN控制器初始化过程,白话点说:
            "a)"CAN是LPC1769的外设器件,要让其工作,首先要设置PCONP,该寄存器的各个位来决定外设
                的时钟是否打开或关闭,若某个外设不被使用,则关闭它,以达到节省功耗的目的;此处要需
           要使用CAN,所以先打开CAN的外设时钟。
            "b)"其次,外设若要正常工作,则均需要一个合适的时钟频率,通PCLK_SEL0l来决定CAN的外设
                时钟来源,以及大小。
            "c)"为了进一步减少MCU的功耗,当CAN网络上没有数据传输时,也没有CAN中断在处理,并且对应
           的睡眠位被置“1”,CAN外设会进入睡眠状态,若CAN总线上出现了显性位,则CAN外设从睡眠
           状态被唤醒。同时,若已配置了相关位,且此时整个MCU都进入掉电或者深度睡眠模式,则CAN
           也可将MCU唤醒
            "d)"配置CAN的收发引脚,无需多言,告诉CAN控制器,从哪个引脚收发CAN数据。
            "e)"配置CAN的各种中断使能条件,此处使能了发送/接收中断,错误中断;以及配置NVIC内
                CAN外设中断。
            "f)"配置CAN相关的参数,譬如波特率等等。
            至此,CAN控制器初始化部分完成,还需要做接收和发送函数,以及中断函数,来实现CAN的收发
        ,和错误管理。
            当然,在CAN控制器初始化部分,波特率的参数设置还有许多要说,本篇篇幅有限,暂不详述,下次
        再开篇博客进行介绍。
            CAN中断函数:
            /*----------------- INTERRUPT SERVICE ROUTINES --------------------------*/
            /*********************************************************************//**
             * @brief        CAN_IRQ Handler, control receive message operation
             * param[in]    none
             * @return         none
             **********************************************************************/
            void CAN_IRQHandler()
            {
                uint8_t IntStatus;
                uint32_t data1;
                /* get interrupt status
                 * Note that: Interrupt register CANICR will be reset after read.
                 * So function "CAN_IntGetStatus" should be call only one time
                 */
                // 以下函数获取的是CAN1ICR/CAN2ICR的寄存器数据,该寄存器指明了中断来源
                IntStatus = CAN_IntGetStatus(LPC_CAN1);if((IntStatus>>0)&0x01) {// 接收中断
                }
                ...
                // 省略的内容为,根据寄存器的各位的中断来源数据来解析中断信息。
           // IntStatus = CAN_IntGetStatus(LPC_CAN2);
           // if(...) ...
            }
            
            CAN接收函数:
            此函数为NXP提供的库函数,库函数下载链接在本文第三部分,该函数做的内容无非就是,在中
        断内,检查两个接收邮箱内是否有信息,若有,则将信息提取。
        /********************************************************************//**
         * @brief        Receive message data
         * @param[in]    CANx pointer to LPC_CAN_TypeDef, should be:
         *                 - LPC_CAN1: CAN1 peripheral
         *                 - LPC_CAN2: CAN2 peripheral
         * @param[in]    CAN_Msg point to the CAN_MSG_Type Struct, it will contain received
         *              message information such as: ID, DLC, RTR, ID Format
         * @return         Status:
         *                 - SUCCESS: receive message successfully
         *                 - ERROR: receive message unsuccessfully
         *********************************************************************/
        Status CAN_ReceiveMsg (LPC_CAN_TypeDef *CANx, CAN_MSG_Type *CAN_Msg)
        {
            uint32_t data;
       
            CHECK_PARAM(PARAM_CANx(CANx));
       
            //check status of Receive Buffer
            if((CANx->SR &0x00000001))
            {
                /* Receive message is available */
                /* Read frame informations */
                CAN_Msg->format   = (uint8_t)(((CANx->RFS) & 0x80000000)>>31);
                CAN_Msg->type     = (uint8_t)(((CANx->RFS) & 0x40000000)>>30);
                CAN_Msg->len      = (uint8_t)(((CANx->RFS) & 0x000F0000)>>16);
       
       
                /* Read CAN message identifier */
                CAN_Msg->id = CANx->RID;
       
                /* Read the data if received message was DATA FRAME */
                if (CAN_Msg->type == DATA_FRAME)
                {
                    /* Read first 4 data bytes */
                    data = CANx->RDA;
                    *((uint8_t *) &CAN_Msg->dataA[0])= data & 0x000000FF;
                    *((uint8_t *) &CAN_Msg->dataA[1])= (data & 0x0000FF00)>>8;;
                    *((uint8_t *) &CAN_Msg->dataA[2])= (data & 0x00FF0000)>>16;
                    *((uint8_t *) &CAN_Msg->dataA[3])= (data & 0xFF000000)>>24;
       
                    /* Read second 4 data bytes */
                    data = CANx->RDB;
                    *((uint8_t *) &CAN_Msg->dataB[0])= data & 0x000000FF;
                    *((uint8_t *) &CAN_Msg->dataB[1])= (data & 0x0000FF00)>>8;
                    *((uint8_t *) &CAN_Msg->dataB[2])= (data & 0x00FF0000)>>16;
                    *((uint8_t *) &CAN_Msg->dataB[3])= (data & 0xFF000000)>>24;
       
                /*release receive buffer*/
                CANx->CMR = 0x04;
                }
                else
                {
                    /* Received Frame is a Remote Frame, not have data, we just receive
                     * message information only */
                    CANx->CMR = 0x04; /*release receive buffer*/
                    return SUCCESS;
                }
            }
            else
            {
                // no receive message available
                return ERROR;
            }
            return SUCCESS;
        }
            
            CAN发送函数:
            该函数还是库函数,即依次查询3个发送邮箱的状态,若邮箱状态为空,则将数据填充到该邮箱
        并置位发送标志,然后由CAN内核模块硬件自动发送。发送的优先级在寄存器内均可配置,不详述。
        篇幅不想过长,因此查询邮箱2/3代码部分省略。
        /********************************************************************//**
         * @brief        Send message data
         * @param[in]    CANx pointer to LPC_CAN_TypeDef, should be:
         *                 - LPC_CAN1: CAN1 peripheral
         *                 - LPC_CAN2: CAN2 peripheral
         * @param[in]    CAN_Msg point to the CAN_MSG_Type Structure, it contains message
         *                 information such as: ID, DLC, RTR, ID Format
         * @return         Status:
         *                 - SUCCESS: send message successfully
         *                 - ERROR: send message unsuccessfully
         *********************************************************************/
        Status CAN_SendMsg (LPC_CAN_TypeDef *CANx, CAN_MSG_Type *CAN_Msg)
        {
            uint32_t data;
            CHECK_PARAM(PARAM_CANx(CANx));
            CHECK_PARAM(PARAM_ID_FORMAT(CAN_Msg->format));
            if(CAN_Msg->format==STD_ID_FORMAT)
            {
                CHECK_PARAM(PARAM_ID_11(CAN_Msg->id));
            }
            else
            {
                CHECK_PARAM(PARAM_ID_29(CAN_Msg->id));
            }
            CHECK_PARAM(PARAM_DLC(CAN_Msg->len));
            CHECK_PARAM(PARAM_FRAME_TYPE(CAN_Msg->type));
       
            //Check status of Transmit Buffer 1
            if (CANx->SR & (1<<2))
            {
                /* Transmit Channel 1 is available */
                /* Write frame informations and frame data into its CANxTFI1,
                 * CANxTID1, CANxTDA1, CANxTDB1 register */
                CANx->TFI1 &= ~0x000F0000;
                CANx->TFI1 |= (CAN_Msg->len)<<16;
                if(CAN_Msg->type == REMOTE_FRAME)
                {
                    CANx->TFI1 |= (1<<30); //set bit RTR
                }
                else
                {
                    CANx->TFI1 &= ~(1<<30);
                }
                if(CAN_Msg->format == EXT_ID_FORMAT)
                {
                    CANx->TFI1 |= (0x80000000); //set bit FF
                }
                else
                {
                    CANx->TFI1 &= ~(0x80000000);
                }
       
                /* Write CAN ID*/
                CANx->TID1 = CAN_Msg->id;
       
                /*Write first 4 data bytes*/
                data = (CAN_Msg->dataA[0])|(((CAN_Msg->dataA[1]))<<8)|
                 ((CAN_Msg->dataA[2])<<16)|((CAN_Msg->dataA[3])<<24);
                CANx->TDA1 = data;
       
                /*Write second 4 data bytes*/
                data = (CAN_Msg->dataB[0])|(((CAN_Msg->dataB[1]))<<8)|
                ((CAN_Msg->dataB[2])<<16)|((CAN_Msg->dataB[3])<<24);
                CANx->TDB1 = data;
       
                 /*Write transmission request*/
                 // 注意该值,置位发送邮箱1,告知硬件,邮箱1的信息已经填充完毕可发送。
                 CANx->CMR = 0x21;
                 return SUCCESS;
            }
            //check status of Transmit Buffer 2
            else if(CANx->SR & (1<<10))
            {
                /* Transmit Channel 2 is available */
                /* Write frame informations and frame data into its CANxTFI2,
                 * CANxTID2, CANxTDA2, CANxTDB2 register */
                 ...
                 /*Write transmission request*/
                 // 注意该值,置位发送邮箱2,告知硬件,邮箱2的信息已经填充完毕可发送。
                CANx->CMR = 0x41;
                return SUCCESS;
            }
            //check status of Transmit Buffer 3
            else if (CANx->SR & (1<<18))
            {
                /* Transmit Channel 3 is available */
                /* Write frame informations and frame data into its CANxTFI3,
                 * CANxTID3, CANxTDA3, CANxTDB3 register */
                 ...
                /*Write transmission request*/
                 // 注意该值,置位发送邮箱3,告知硬件,邮箱3的信息已经填充完毕可发送。
                CANx->CMR = 0x81;
                return SUCCESS;
            }
            else
            {
                // 所有邮箱都处于非空闲状态,无法发送
                return ERROR;
            }
        }

        至此,有了初始化部分,CAN中断函数,CAN发送、接收函数,也就实现了CAN数据的收发。
        滤波以波特率以及CAN总线错误处理,下次再开博客详述。

    三、参考文档
        LPC175x_6x CMSIS-Compliant Standard Peripheral Firmware Driver Library (Keil, IAR, GNU)
          http://www.lpcware.com/content/n ... ibrary-keil-iar-gnu


    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    该用户从未签到

    1

    主题

    2

    帖子

    0

    新手上路

    Rank: 1

    积分
    12
    最后登录
    2022-2-11
    发表于 2020-10-22 11:15:48 | 显示全部楼层
    您好,我这有LPC1778芯片的CAN1通道通讯程序,请问我需要改为CAN2通道进行通讯应该怎么改呀
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2025-8-8 16:43
  • 签到天数: 1504 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    97

    主题

    4693

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    10105
    最后登录
    2025-9-11
     楼主| 发表于 2020-10-23 09:05:48 | 显示全部楼层
    mfy111 发表于 2020-10-22 11:15
    您好,我这有LPC1778芯片的CAN1通道通讯程序,请问我需要改为CAN2通道进行通讯应该怎么改呀 ...

    将配置CAN1外设的代码修改为配置CAN2外设的代码
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    0

    主题

    1

    帖子

    0

    新手上路

    Rank: 1

    积分
    11
    最后登录
    2022-7-27
    发表于 2022-7-27 12:08:30 | 显示全部楼层
    您好,我想问问和您的can的配置差不多,就是进不了can接收中断
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2025-8-8 16:43
  • 签到天数: 1504 天

    连续签到: 1 天

    [LV.Master]伴坛终老

    97

    主题

    4693

    帖子

    12

    版主

    Rank: 7Rank: 7Rank: 7

    积分
    10105
    最后登录
    2025-9-11
     楼主| 发表于 2022-7-27 15:43:15 | 显示全部楼层
    eefocus_3857234 发表于 2022-7-27 12:08
    您好,我想问问和您的can的配置差不多,就是进不了can接收中断

    进不去中断按顺序查:全局中断是否打开,CAN中断是否打开,CAN引脚配置是否正确
    该会员没有填写今日想说内容.
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-9-12 15:39 , Processed in 0.121029 second(s), 23 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表