通过设置 CAN 中断允许寄存器 CAN_IER ,每个中断源都可以单独允许和禁 用。 (1) 发送中断可由下列事件产生: ─ 发送邮箱 0 变为空,CAN_TSR 寄存器的 RQCP0 位被置 1。 ─ 发送邮箱 1 变为空,CAN_TSR 寄存器的 RQCP1 位被置 1。 ─ 发送邮箱 2 变为空,CAN_TSR 寄存器的 RQCP2 位被置 1。 (2) FIFO0 中断可由下列事件产生: ─ FIFO0 接收到一个新报文,CAN_RF0R 寄存器的 FMP0 位不再是‘00’。 ─ FIFO0 变为满的情况,CAN_RF0R 寄存器的 FULL0 位被置 1。 ─ FIFO0 发生溢出的情况,CAN_RF0R 寄存器的 FOVR0 位被置 1。 (3) FIFO1 中断可由下列事件产生: ─ FIFO1 接收到一个新报文,CAN_RF1R 寄存器的 FMP1 位不再是‘00’。 ─ FIFO1 变为满的情况,CAN_RF1R 寄存器的 FULL1 位被置 1。 ─ FIFO1 发生溢出的情况,CAN_RF1R 寄存器的 FOVR1 位被置 1。 (4) 错误和状态变化中断可由下列事件产生: ─ 出错情况,关于出错情况的详细信息请参考 CAN 错误状态寄存器(CAN_ESR)。 ─ 唤醒情况,在 CAN 接收引脚上监视到帧起始位(SOF)。 ─ CAN 进入睡眠模式。 工作流程大概就是这个样子,接着就是一大堆烦人的 can 寄存器,看了一遍总算有了大概的了解,况且这么多的 寄存器要一下子把他们都记住是不可能的。根据以往的经验,只要用多几次,对寄存器的功能就能记住。 好了,到读具体实验程序的时候了,这时候当然要打开“STM32 库函数”的资料,因为它里面有 STM32 打包好的库 函数的解释,对读程序很有帮助。 下面是主程序: int main(void) { // int press_count = 0; char data = '0'; int sent = FALSE; #ifdef DEBUG debug(); #endif RCC_Configuration(); NVIC_Configuration(); GPIO_Configuration(); USART_Configuration(); CAN_Configuration(); Serial_PutString("\r\n 伟研科技 http://www.gzweiyan.com\r\n"); Serial_PutString("CAN test\r\n"); while(1) { if(GPIO_Keypress(GPIO_KEY, BUT_RIGHT)) { GPIO_SetBits(GPIO_LED, GPIO_LD1); //检测到按键按下 if(sent == TRUE) continue; sent = TRUE; data ++; if(data > 'z') data = '0'; CAN_TxData(data); } else //按键放开 { GPIO_ResetBits(GPIO_LED, GPIO_LD1); sent = FALSE; } } } 前面的 RCC、NVIC、GPIO、USART 配置和之前的实验大同小异,关键是分析 CAN_Configuration() 函数如下: void CAN_Configuration(void) //CAN 配置函数 { CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; CAN_DeInit(); // CAN_StructInit(&CAN_InitStructure); CAN_InitStruc ture.CAN _TTCM=DISABLE;// 禁止时间触发通信模式 CAN_InitStructure.CAN_ABOM=DISABLE;//软件对 CAN_MCR 寄存器的 INRQ 位进行置 1 随后清 0 后,一旦硬 件检测 //到 128 次 11 位连续的隐性位,就退出离线状态。 CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过清除 CAN_MCR 寄存器的 SLEEP 位,由软件唤醒 CAN_InitStructure.CAN_NART=ENABLE;//DISABLE; CAN 报文只被发送 1 次,不管发送的结果如何(成功、出 错或仲裁丢失) CAN_InitStructure.CAN_RFLM=DISABLE;//在接收溢出时 FIFO 未被锁定,当接收 FIFO 的报文未被读出,下一 个收到的报文会覆盖原有的报文 CAN_InitStructure.CAN_TXFP=DISABLE;//发送 FIFO 优先级由报文的标识符来决定 // CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack; CAN_InitStructure.CAN_Mode=CAN_Mode_Normal; //CAN 硬件工作在正常模式 CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度 1 个时间单位 CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;//时间段 1 为 8 个时间单位 CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//时间段 2 为 7 个时间单位 CAN_InitStructure.CAN_Prescaler = 9; //(pclk1/((1+8+7)*9)) = 36Mhz/16/9 = 250Kbits 设定了一个时间单位的长度 9 CAN_Init(&CAN_InitStructure); CAN_FilterInitStructure.CAN_FilterNumber=0;//指定了待初始化的过滤器 0 CAN_FilterInitStructure.CAN_Filte rMode=CAN_FilterMode_IdMa s k;//指定 了过滤 器将被 初始化 到的模 式标识 符屏 蔽位模式 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//给出了过滤器位宽 1 个 32 位过滤器 CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;//用来设定过滤器标识符(32 位位宽时为其高段位,16 位位宽 时为第一个) CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;//用来设定过滤器标识符(32 位位宽时为其低段位,16 位位宽 时为第二个 CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//用来设定过滤器屏蔽标识符或者过滤器标识符(32 位位 宽时为其高段位,16 位位宽时为第一个) CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;//用来设定过滤器屏蔽标识符或者过滤器标识符(32 位位 宽时为其低段位,16 位位宽时为第二个 CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;//设定了指向过滤器的 FIFO0 CAN_FilterInitStructure.CAN_Filte rAc tiva tion=ENABLE;// 使能过滤器 CAN_FilterInit(&CAN_FilterInitStructure); CAN_ITConfig(CAN_IT_FMP0, ENABLE);//使能指定的 CAN 中断 } 再看看发送程序: TestStatus CAN_TxData(char data) { CanTxMsg TxMessage; u32 i = 0; u8 TransmitMailbox = 0; TxMessage.StdId=0x00;// 设定标准标识符 TxMessage.ExtId=0x1234;// 设定扩展标识符 TxMessage.IDE=CAN_ID_EXT;// 设定消息标识符的类型 TxMessage.RTR=CAN_RTR_DATA;// 设定待传输消息的帧类型 TxMessage.DLC= 1; //设定待传输消息的帧长度 TxMessage.Data[0] = data;// 包含了待传输数据 TransmitMailbox = CAN_Transmit(&TxMessage);//开始一个消息的传输 i = 0; while((CAN_TransmitStatus(TransmitMailbox) !=