首页 > 代码库 > LPC1768/1769之CAN控制器概述
LPC1768/1769之CAN控制器概述
一、背景: 使用LPC1769来做CAN的收发,在此对使用LPC1769的CAN控制器进行收发做个总结和记录,以备下 次开发快速上手使用。 附:LPC1768/1769除了支持最高频率不同以外,其它基本上一致。 二、正文: 先贴一张LPC1769 CAN控制器的方框图:
.
由上图可见,整个CAN控制器一头是CPU,另一头是CAN收发器:
CAN收发器负责CAN数据与CAN网络的通信。CAN内核模块解析和封装要发送到CAN收发器以及从CAN
收发器发过来的数据,此处CAN内核工作由硬件自行完成。
CPU通过APB总线即可设置CAN控制器状态,以及读取中断信息和中断状态。
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); IntStatus = CAN_IntGetStatus(LPC_CAN2); if((IntStatus>>0)&0x01) {// 接收中断 } ... // 省略的内容为,根据寄存器的各位的中断来源数据来解析中断信息。 } 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) https://www.lpcware.com/content/nxpfile/lpc175x6x-cmsis-compliant-standard-peripheral-firmware-driver-library-keil-iar-gnu 至此,记录完毕。 记录时间:2016-11-28 记录地点:深圳WZ
LPC1768/1769之CAN控制器概述
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。