HAL_StatusTypeDef HAL_CAN_Transmit(CAN_HandleTypeDef* hcan, uint32_t Timeout)
565 {
566 uint32_t transmitmailbox = CAN_TXSTATUS_NOMAILBOX;
567 uint32_t tickstart = 0U;
568
569 /* Check the parameters */
570 assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE));
571 assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR));
572 assert_param(IS_CAN_DLC(hcan->pTxMsg->DLC));
573
574 if(((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) || \
575 ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) || \
576 ((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2))
577 {
578 /* Process locked */
579 __HAL_LOCK(hcan);
580
581 /* Change CAN state */
582 switch(hcan->State)
583 {
584 case(HAL_CAN_STATE_BUSY_RX0):
585 hcan->State = HAL_CAN_STATE_BUSY_TX_RX0;
586 break;
587 case(HAL_CAN_STATE_BUSY_RX1):
588 hcan->State = HAL_CAN_STATE_BUSY_TX_RX1;
589 break;
590 case(HAL_CAN_STATE_BUSY_RX0_RX1):
591 hcan->State = HAL_CAN_STATE_BUSY_TX_RX0_RX1;
592 break;
593 default: /* HAL_CAN_STATE_READY */
594 hcan->State = HAL_CAN_STATE_BUSY_TX;
595 break;
596 }
597
598 /* Select one empty transmit mailbox */
599 if ((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
600 {
601 transmitmailbox = CAN_TXMAILBOX_0;
602 }
603 else if ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
604 {
605 transmitmailbox = CAN_TXMAILBOX_1;
606 }
607 else
608 {
609 transmitmailbox = CAN_TXMAILBOX_2;
610 }
611
612 /* Set up the Id */
613 hcan->Instance->sTxMailBox[transmitmailbox].TIR &= CAN_TI0R_TXRQ;
614 if (hcan->pTxMsg->IDE == CAN_ID_STD)
615 {
616 assert_param(IS_CAN_STDID(hcan->pTxMsg->StdId));
617 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->StdId << 21U) | \
618 hcan->pTxMsg->RTR);
619 }
620 else
621 {
622 assert_param(IS_CAN_EXTID(hcan->pTxMsg->ExtId));
623 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->ExtId << 3U) | \
624 hcan->pTxMsg->IDE | \
625 hcan->pTxMsg->RTR);
626 }
627
628 /* Set up the DLC */
629 hcan->pTxMsg->DLC &= (uint8_t)0x0000000F;
630 hcan->Instance->sTxMailBox[transmitmailbox].TDTR &= (uint32_t)0xFFFFFFF0U;
631 hcan->Instance->sTxMailBox[transmitmailbox].TDTR |= hcan->pTxMsg->DLC;
632
633 /* Set up the data field */
634 hcan->Instance->sTxMailBox[transmitmailbox].TDLR = (((uint32_t)hcan->pTxMsg->Data[3U] << 24U) |
635 ((uint32_t)hcan->pTxMsg->Data[2U] << 16U) |
636 ((uint32_t)hcan->pTxMsg->Data[1U] << 8U) |
637 ((uint32_t)hcan->pTxMsg->Data[0U]));
638 hcan->Instance->sTxMailBox[transmitmailbox].TDHR = (((uint32_t)hcan->pTxMsg->Data[7U] << 24U) |
639 ((uint32_t)hcan->pTxMsg->Data[6U] << 16U) |
640 ((uint32_t)hcan->pTxMsg->Data[5U] << 8U) |
641 ((uint32_t)hcan->pTxMsg->Data[4U]));
642 /* Request transmission */
643 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ;
644
645 /* Get tick */
646 tickstart = HAL_GetTick();
647
648 /* Check End of transmission flag */
649 while(!(__HAL_CAN_TRANSMIT_STATUS(hcan, transmitmailbox)))
650 {
651 /* Check for the Timeout */
652 if(Timeout != HAL_MAX_DELAY)
653 {
654 if((Timeout == 0U)||((HAL_GetTick() - tickstart ) > Timeout))
655 {
656 hcan->State = HAL_CAN_STATE_TIMEOUT;
657
658 __HAL_CAN_CANCEL_TRANSMIT(hcan, transmitmailbox);
659
660 /* Process unlocked */
661 __HAL_UNLOCK(hcan);
662 return HAL_TIMEOUT;
663 }
664 }
665 }
666
667 /* Change CAN state */
668 switch(hcan->State)
669 {
670 case(HAL_CAN_STATE_BUSY_TX_RX0):
671 hcan->State = HAL_CAN_STATE_BUSY_RX0;
672 break;
673 case(HAL_CAN_STATE_BUSY_TX_RX1):
674 hcan->State = HAL_CAN_STATE_BUSY_RX1;
675 break;
676 case(HAL_CAN_STATE_BUSY_TX_RX0_RX1):
677 hcan->State = HAL_CAN_STATE_BUSY_RX0_RX1;
678 break;
679 default: /* HAL_CAN_STATE_BUSY_TX */
680 hcan->State = HAL_CAN_STATE_READY;
681 break;
682 }
683
684 /* Process unlocked */
685 __HAL_UNLOCK(hcan);
686
687 /* Return function status */
688 return HAL_OK;
689 }
690 else
691 {
692 /* Change CAN state */
693 hcan->State = HAL_CAN_STATE_ERROR;
694
695 /* Return function status */
696 return HAL_ERROR;
697 }
698 }
代码分析:
1. 检查参数,根据TSR发送状态寄存器,判断可用的发送邮箱的个数,如果有可用邮箱的话就继续往下走,没有的话就报错.
2. 由小往大选定一个可用的邮箱.
3. 对邮箱的TIR寄存,标识寄存器器进行配置,主要是ID和发送的帧的数据类型,数据帧还是遥控帧.
4. 将数据的长度放到长度控制和时间戳控制寄存器TDTR
5. 将8个字节的数据放到邮箱的数据寄存器,分别是TDLR,TDHR,每个寄存器各放4个字节的数据.
6. 将TIR寄存器的TXRQ置位,请求发送邮箱里面的数据.
7. 循环等待,CAN_TSR_RQCP(上一次发送成功置位),CAN_TSR_TXOK(邮箱请求完成置位),CAN_TSR_TME(邮箱为空置位)这3个寄存器是否被置位.并且判断整个发送过程中是否超时.
8. 根据状态返回值.
706 HAL_StatusTypeDef HAL_CAN_Transmit_IT(CAN_HandleTypeDef* hcan)
707 {
708 uint32_t transmitmailbox = CAN_TXSTATUS_NOMAILBOX;
709
710 /* Check the parameters */
711 assert_param(IS_CAN_IDTYPE(hcan->pTxMsg->IDE));
712 assert_param(IS_CAN_RTR(hcan->pTxMsg->RTR));
713 assert_param(IS_CAN_DLC(hcan->pTxMsg->DLC));
714
715 if(((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0) || \
716 ((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1) || \
717 ((hcan->Instance->TSR&CAN_TSR_TME2) == CAN_TSR_TME2))
718 {
719 /* Process Locked */
720 __HAL_LOCK(hcan);
721
722 /* Select one empty transmit mailbox */
723 if((hcan->Instance->TSR&CAN_TSR_TME0) == CAN_TSR_TME0)
724 {
725 transmitmailbox = CAN_TXMAILBOX_0;
726 }
727 else if((hcan->Instance->TSR&CAN_TSR_TME1) == CAN_TSR_TME1)
728 {
729 transmitmailbox = CAN_TXMAILBOX_1;
730 }
731 else
732 {
733 transmitmailbox = CAN_TXMAILBOX_2;
734 }
735
736 /* Set up the Id */
737 hcan->Instance->sTxMailBox[transmitmailbox].TIR &= CAN_TI0R_TXRQ;
738 if(hcan->pTxMsg->IDE == CAN_ID_STD)
739 {
740 assert_param(IS_CAN_STDID(hcan->pTxMsg->StdId));
741 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->StdId << 21U) | \
742 hcan->pTxMsg->RTR);
743 }
744 else
745 {
746 assert_param(IS_CAN_EXTID(hcan->pTxMsg->ExtId));
747 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= ((hcan->pTxMsg->ExtId << 3U) | \
748 hcan->pTxMsg->IDE | \
749 hcan->pTxMsg->RTR);
750 }
751
752 /* Set up the DLC */
753 hcan->pTxMsg->DLC &= (uint8_t)0x0000000F;
754 hcan->Instance->sTxMailBox[transmitmailbox].TDTR &= (uint32_t)0xFFFFFFF0U;
755 hcan->Instance->sTxMailBox[transmitmailbox].TDTR |= hcan->pTxMsg->DLC;
756
757 /* Set up the data field */
758 hcan->Instance->sTxMailBox[transmitmailbox].TDLR = (((uint32_t)hcan->pTxMsg->Data[3U] << 24U) |
759 ((uint32_t)hcan->pTxMsg->Data[2U] << 16U) |
760 ((uint32_t)hcan->pTxMsg->Data[1U] << 8U) |
761 ((uint32_t)hcan->pTxMsg->Data[0U]));
762 hcan->Instance->sTxMailBox[transmitmailbox].TDHR = (((uint32_t)hcan->pTxMsg->Data[7U] << 24U) |
763 ((uint32_t)hcan->pTxMsg->Data[6U] << 16U) |
764 ((uint32_t)hcan->pTxMsg->Data[5U] << 8U) |
765 ((uint32_t)hcan->pTxMsg->Data[4U]));
766
767 /* Change CAN state */
768 switch(hcan->State)
769 {
770 case(HAL_CAN_STATE_BUSY_RX0):
771 hcan->State = HAL_CAN_STATE_BUSY_TX_RX0;
772 break;
773 case(HAL_CAN_STATE_BUSY_RX1):
774 hcan->State = HAL_CAN_STATE_BUSY_TX_RX1;
775 break;
776 case(HAL_CAN_STATE_BUSY_RX0_RX1):
777 hcan->State = HAL_CAN_STATE_BUSY_TX_RX0_RX1;
778 break;
779 default: /* HAL_CAN_STATE_READY */
780 hcan->State = HAL_CAN_STATE_BUSY_TX;
781 break;
782 }
783
784 /* Set CAN error code to none */
785 hcan->ErrorCode = HAL_CAN_ERROR_NONE;
786
787 /* Process Unlocked */
788 __HAL_UNLOCK(hcan);
789
790 /* Request transmission */
791 hcan->Instance->sTxMailBox[transmitmailbox].TIR |= CAN_TI0R_TXRQ;
792
793 /* Enable Error warning, Error passive, Bus-off,
794 Last error and Error Interrupts */
795 __HAL_CAN_ENABLE_IT(hcan, CAN_IT_EWG |
796 CAN_IT_EPV |
797 CAN_IT_BOF |
798 CAN_IT_LEC |
799 CAN_IT_ERR |
800 CAN_IT_TME);
801 }
802 else
803 {
804 /* Change CAN state */
805 hcan->State = HAL_CAN_STATE_ERROR;
806
807 /* Return function status */
808 return HAL_ERROR;
809 }
810
811 return HAL_OK;
812 }
代码分析:
前面1-6的步骤和阻塞是发送都是一样的.
7. 开启中断
一帧数据发送完成之后,进入can的中断.
注意开启的中断:
449 #define CAN_IT_TME ((uint32_t)CAN_IER_TMEIE) /*!< Transmit mailbox empty interrupt */
463 /* Error Interrupts */
464 #define CAN_IT_EWG ((uint32_t)CAN_IER_EWGIE) /*!< Error warning interrupt */
465 #define CAN_IT_EPV ((uint32_t)CAN_IER_EPVIE) /*!< Error passive interrupt */
466 #define CAN_IT_BOF ((uint32_t)CAN_IER_BOFIE) /*!< Bus-off interrupt */
467 #define CAN_IT_LEC ((uint32_t)CAN_IER_LECIE) /*!< Last error code interrupt */
468 #define CAN_IT_ERR ((uint32_t)CAN_IER_ERRIE) /*!< Error Interrupt */
一个发送邮箱空中断,还有5种错误情况引起的中断
1251 if(__HAL_CAN_GET_IT_SOURCE(hcan, CAN_IT_TME))
1252 {
1253 tmp1 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_0);
1254 tmp2 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_1);
1255 tmp3 = __HAL_CAN_TRANSMIT_STATUS(hcan, CAN_TXMAILBOX_2);
1256 if(tmp1 || tmp2 || tmp3)
1257 {
1258 tmp1 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK0);
1259 tmp2 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK1);
1260 tmp3 = __HAL_CAN_GET_FLAG(hcan, CAN_FLAG_TXOK2);
1261 /* Check Transmit success */
1262 if(tmp1 || tmp2 || tmp3)
1263 {
1264 /* Call transmit function */
1265 CAN_Transmit_IT(hcan);
1266 }
1267 else /* Transmit failure */
1268 {
1269 /* Set CAN error code to TXFAIL error */
1270 errorcode |= HAL_CAN_ERROR_TXFAIL;
1271 }
1272
1273 /* Clear transmission status flags (RQCPx and TXOKx) */
1274 SET_BIT(hcan->Instance->TSR, CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2 | \
1275 CAN_FLAG_TXOK0 | CAN_FLAG_TXOK1 | CAN_FLAG_TXOK2);
1276 }
1277 }
其中判断,发送是否完成的代码.
1. 第一句话还是和之前的一样,读取到三个邮箱的状态,也就是RQCP, TXOK,TME,三个寄存器的状态.
2. 第二句话就是进一步去判断,TXOK寄存器,检查三个寄存器中是否有发送完成的邮箱.
具体的判断逻辑如上所示,也就是去检查TSR这个寄存器的第1,9,11位来判断是否发送完成.如果发送完成就可去调用CAN_Transmit_IT,在这个里面主要是关中断,再调用用户的回调函数.
3. 清掉RQCP, TXOK寄存器的标志位
题外话:
公司的代码,移植的是can的驱动代码,里面有一句话让我觉得很奇怪:
if(HAL_CAN_Transmit(&g_sCAN_Handler[dwDevice], 10) != HAL_OK)
{//注:如果发送中断使能,因在发送中断里会清相关标志,这样会导致此函数会超时,而发送实际是成功的
return FALSE;
}
为什么会有这样一句奇怪的注释,这里使用的阻塞的方式发送数据,那么计算超时的时候就会检查RQCP, TXOK,这两个位用来判断是否发送完成。但是在hal库里面没有发送时中断是关掉了,根本就不会在中断里面清掉标志位,根本就不存在这个问题。当我们使用中断的方式发送时, 又不会进行超时判断。因此只有在使用不规范的时候才会出现这个问题,因此这句注释就是无稽之谈。