[学习笔记] 寄存器学习之can

[复制链接]
 楼主| JasonLee27 发表于 2020-7-27 14:11 | 显示全部楼层 |阅读模式
硬件环境:AC7801x 通用开发板  ATC-LINK
软件环境:keil 5.23


上一章节完成了clock,让芯片运行在48M PLL下,这次我们主要完成下面三个工作:
1,让延时更精确,将采用systick进行延时,实现延时功能
2,增加休眠唤醒,让芯片可以进入休眠以及唤醒
3,增加CAN模块的收发功能

一,使用systick进行延时,可直接参照驱动中的实现。
  1. /*!
  2. * [url=home.php?mod=space&uid=247401]@brief[/url] delay until the syctick count tick to 0
  3. *
  4. * @param[in] tick : systick count value
  5. * [url=home.php?mod=space&uid=266161]@return[/url] none
  6. */
  7. static void SysTickDelay(uint32_t tick)
  8. {
  9.     __IO uint32_t tickFlag = 0;

  10.     SysTick->LOAD = tick - 1;
  11.     SysTick->VAL = 0x00;
  12.     SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
  13.     do
  14.     {
  15.         tickFlag = SysTick->CTRL;

  16.     } while(!(tickFlag & SysTick_CTRL_COUNTFLAG_Msk));

  17.     SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
  18.     SysTick->VAL = 0X00;
  19. }

  20. /*!
  21. * [url=home.php?mod=space&uid=247401]@brief[/url] Repeatlly delay the ticks for the given times
  22. *
  23. * @param[in] param : times, the times of delay the tick
  24. * @param[in] param : tick, each tick to delay
  25. * [url=home.php?mod=space&uid=266161]@return[/url] none
  26. */
  27. static void SysTickRepeatDelay(uint32_t times, uint32_t tick)
  28. {
  29.     uint32_t i = 0;

  30.     for (i = 0; i < times; i++)
  31.     {
  32.         SysTickDelay(tick);
  33.     }
  34. }

  35. /*!
  36. * @brief delay us
  37. *
  38. * @param[in] param : us, us for delay
  39. * @return none
  40. */
  41. void udelay(uint32_t us)
  42. {
  43.     uint32_t tick = us * s_facus;

  44.     SysTickRepeatDelay(tick / MAX_SYSTICK_COUNT, MAX_SYSTICK_COUNT);

  45.     SysTickDelay(tick % MAX_SYSTICK_COUNT);
  46. }

  47. /*!
  48. * @brief delay ms
  49. *
  50. * @param[in] param : ms, us for delay
  51. * @return none
  52. */
  53. void mdelay(uint32_t ms)
  54. {
  55.     uint32_t tick = ms * s_facms;

  56.     SysTickRepeatDelay(tick / MAX_SYSTICK_COUNT, MAX_SYSTICK_COUNT);

  57.     SysTickDelay(tick % MAX_SYSTICK_COUNT);
  58. }
二,芯片进入stop模式
此处设置stop1,同时使能spm,关闭LVD是为了让功耗更低,第三行似乎是内部的一些操作,也保留了下来。
最后就是arm内核的休眠操作了。

  1. void sysStop(void)
  2. {
  3.     MODIFY_REG32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_SLEEP_MODE_Msk, SPM_PWR_MGR_CFG0_SLEEP_MODE_Pos, 1);   ///<set stop 1 mode
  4.     SET_BIT32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_PWR_EN_Msk);  ///<enable spm
  5.     MODIFY_MEM32(REG_PORLPVD_CFG0_ADDR, LOW_POWER_CFG_MASK, LOW_POWER_CFG_START_BIT, LOW_POWER_CFG_VALUE);
  6.     CLEAR_BIT32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_DPWRLVD_Msk);    ///<disable LVD
  7.    
  8.     /* Set the SLEEPDEEP bit to enable deep sleep mode (STOP) */
  9.     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
  10.    
  11.     // If using KEIL's uVision, use the CMSIS intrinsic
  12.     __wfi();
  13. }
当然,在进入休眠前,少不了要使能一些唤醒,否则就起不来了,所以在外层再封装一个函数,此处使能了GPIO以及CAN唤醒
  1. void Mcu_GotoLowPower(void)
  2. {
  3.     SPM->EN_PERIPH_WAKEUP |= SPM_EN_PERIPH_WAKEUP_CAN0_Msk | SPM_EN_PERIPH_WAKEUP_GPIO_Msk; ///<enable wakeup
  4.     sysStop();
  5.    
  6. }
三,can实现,对于can实现的具体就不细说了,对于一般的使用来说,can确实没有太多东西需要初始化的。在can.h文件中实现了如下接口。
  1. typedef struct _can_msg
  2. {
  3.     uint32_t    ID;
  4.     uint32_t     DLC:4;                               /*!< Data length code */
  5.     uint32_t     FDF:1;                               /*!< FD format indicator */
  6.     uint32_t     IDE:1;                               /*!< Identifier extension */
  7.     uint32_t     reserved:26;
  8.     uint8_t     DATA[64];                             /*!< Data must 4bytes align*/
  9. }CanMsgType;
  10.    
  11. extern void Can_Init(void);
  12.    
  13. extern void Can_DeInit(void);

  14. extern int Can_Transmit(CanMsgType *canMsg);

  15. extern int Can_Receive(CanMsgType *canMsg);
整体工程实现过程中遇到如下问题:
1,can模块DATA域必须4字节对齐
在cortex-m0+中是不支持非对齐访问的,而can模块的RBUF和TBUF又都是按照4字节访问,所以在访问时我们需要将我们定义的结构体中的DATA成员转为uint32,如果DATA未四字节对齐,将会导致非对齐访问错误而进入hardfault
  1. CANx->TBUF.DATA[i>>2] = *(uint32_t *)(&canMsg->DATA[i]);
所以在定义can结构体时,需要在前面填充,以确保声明的DATA[64]起始地址是4字节对齐的

2,can模块初始化时,部分寄存器在RESET=1时初始化,部分在RESET=0时初始化
这涉及到CAN模块的软件复位功能,在参考手册ATC_AC7801x_ReferenceManual_CH.pdf 的7.3.11节有说明。这里主要是设置CAN波特率的寄存器,以及CANFD选择寄存器需要在RESET=1下写入,RESET=0时就锁定了。
  1. void Can_Init(void)
  2. {
  3.     CKGEN->CTRL |= CKGEN_CTRL_CAN0_CLK_SEL_Msk;
  4.     CKGEN->PERI_CLK_EN_0 |= CKGEN_PERI_CLK_EN_0_CAN0_EN_Msk;    ///<CAN clock en
  5.     CKGEN->PERI_SFT_RST0 |= CKGEN_PERI_SFT_RST0_SRST_CAN0_Msk;
  6.    
  7.     CANx->CTRL0 |= CAN_CTRL0_RESET_Msk; ///<CAN set reset some register must write while RESET=1 and lock while RESET=0
  8.    
  9.     CANx->CTRL0 |= CAN_CTRL0_FDISO_Msk; ///<CANFD
  10.     /*
  11.         tSeg1 = (S_SEG_1 + 2); tSeg2 = (S_SEG_2 + 1).
  12.         BandRate         = (48M / (S_PRESC + 1) / ((S_SEG_1 + 2) + (S_SEG_2 + 1))) = 500K
  13.         SamplePoint = (tSeg1 / (tSeg1 + tSeg2)) = 81.25%.
  14.     */
  15.     CANx->SBITRATE = (5 << CAN_BITRATE_PRESC_Pos) + (2 << CAN_BITRATE_SJW_Pos) + (2 << CAN_BITRATE_SEG_2_Pos) + 11; ///<Low speed
  16.    
  17.     ///<1M  81.25%
  18.     CANx->FBITRATE = (2 << CAN_BITRATE_PRESC_Pos) + (2 << CAN_BITRATE_SJW_Pos) + (2 << CAN_BITRATE_SEG_2_Pos) + 11; ///<High speed
  19.    
  20.    
  21.     CANx->CTRL0 &= ~CAN_CTRL0_RESET_Msk; ///<CAN reset
  22.    
  23.    
  24.     CANx->CTRL1 &= ~0xFE;   ///<clear all interrupt enable
  25.    
  26.     CANx->CTRL1 |= (CAN_CTRL1_EIE_Msk | CAN_CTRL1_RIE_Msk | CAN_CTRL1_TSIE_Msk | CAN_CTRL1_TPIE_Msk);   ///<enable send receive and error interrupt
  27.    
  28.     CANx->CTRL1 |= (15 << CAN_CTRL1_EWL_Pos);   ///<EIF limit value set 128 ((EWL+1)*8)
  29.    
  30.     NVIC_EnableIRQ(CAN0_IRQn);
  31. }
3,对于can模块,只使用STB发送,接收未设软件buff,因为STB本身已经有一个3深度的发送缓冲区,同时接收缓冲区的深度为7,加快一下芯片接收处理的速度,完全可以做到不需要软件buff,再不济可以使用硬件过滤,去除不需要接收的ID,更能减少接收的报文,避免溢出。

话说,代码好像挺多了。
Program Size: Code=2024 RO-data=240 RW-data=8 ZI-data=1264  

can.rar (371.9 KB, 下载次数: 55)

zeshoufx 发表于 2020-7-28 14:33 | 显示全部楼层
谢谢分享【 寄存器学习之can】
guanxiangli 发表于 2022-1-12 11:30 | 显示全部楼层
我也在搞CAN唤醒,请问楼主配置完这些后只要发送数据触发中断就会唤醒MCU吗
 楼主| JasonLee27 发表于 2022-1-12 18:04 | 显示全部楼层
guanxiangli 发表于 2022-1-12 11:30
我也在搞CAN唤醒,请问楼主配置完这些后只要发送数据触发中断就会唤醒MCU吗 ...

CAN唤醒功能实际上是由SPM模块实现,CAN模块不开都没关系,关键是CAN RX引脚要配置为CAN RX功能,然后SPM模块要使能CAN唤醒。休眠后RX脚为低电平唤醒。拉低CAN RX脚就可以唤醒了
tail066 发表于 2022-2-14 10:32 | 显示全部楼层
感谢分享
后面会不会出连载,哈哈哈哈~
内政奇才 发表于 2022-2-14 11:37 来自手机 | 显示全部楼层
学习了
内政奇才 发表于 2022-2-14 11:38 来自手机 | 显示全部楼层
学习了
维维biu 发表于 2022-4-25 10:26 | 显示全部楼层
回环能发送,正常模式发送不出去什么情况
cyclefly 发表于 2022-5-17 21:09 | 显示全部楼层
什么是回环
两只袜子 发表于 2022-5-18 09:50 | 显示全部楼层
能把代码框换一种颜色吗?看着很吃力
sadicy 发表于 2022-6-5 09:44 | 显示全部楼层
can和lin在车上基本就用这两种吧
koala889 发表于 2022-6-13 21:19 | 显示全部楼层
好贴需要反复看
andygirl 发表于 2022-6-14 21:18 | 显示全部楼层
真不错啊~
linco-691 发表于 2025-4-1 11:08 | 显示全部楼层
请问一下,在MCU进入stop模式,使用SPM使能can唤醒之后,发现can报文可以唤醒MCU,但是无法进入到can的接收中断,这是什么原因导致的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

66

主题

415

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部