硬件环境:AC7801x 通用开发板 ATC-LINK
软件环境:keil 5.23
上一章节完成了clock,让芯片运行在48M PLL下,这次我们主要完成下面三个工作:
1,让延时更精确,将采用systick进行延时,实现延时功能
2,增加休眠唤醒,让芯片可以进入休眠以及唤醒
3,增加CAN模块的收发功能
一,使用systick进行延时,可直接参照驱动中的实现。
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] delay until the syctick count tick to 0
*
* @param[in] tick : systick count value
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
static void SysTickDelay(uint32_t tick)
{
__IO uint32_t tickFlag = 0;
SysTick->LOAD = tick - 1;
SysTick->VAL = 0x00;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
do
{
tickFlag = SysTick->CTRL;
} while(!(tickFlag & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0X00;
}
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] Repeatlly delay the ticks for the given times
*
* @param[in] param : times, the times of delay the tick
* @param[in] param : tick, each tick to delay
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
static void SysTickRepeatDelay(uint32_t times, uint32_t tick)
{
uint32_t i = 0;
for (i = 0; i < times; i++)
{
SysTickDelay(tick);
}
}
/*!
* @brief delay us
*
* @param[in] param : us, us for delay
* @return none
*/
void udelay(uint32_t us)
{
uint32_t tick = us * s_facus;
SysTickRepeatDelay(tick / MAX_SYSTICK_COUNT, MAX_SYSTICK_COUNT);
SysTickDelay(tick % MAX_SYSTICK_COUNT);
}
/*!
* @brief delay ms
*
* @param[in] param : ms, us for delay
* @return none
*/
void mdelay(uint32_t ms)
{
uint32_t tick = ms * s_facms;
SysTickRepeatDelay(tick / MAX_SYSTICK_COUNT, MAX_SYSTICK_COUNT);
SysTickDelay(tick % MAX_SYSTICK_COUNT);
}
二,芯片进入stop模式
此处设置stop1,同时使能spm,关闭LVD是为了让功耗更低,第三行似乎是内部的一些操作,也保留了下来。
最后就是arm内核的休眠操作了。
void sysStop(void)
{
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
SET_BIT32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_PWR_EN_Msk); ///<enable spm
MODIFY_MEM32(REG_PORLPVD_CFG0_ADDR, LOW_POWER_CFG_MASK, LOW_POWER_CFG_START_BIT, LOW_POWER_CFG_VALUE);
CLEAR_BIT32(SPM->PWR_MGR_CFG0, SPM_PWR_MGR_CFG0_EN_DPWRLVD_Msk); ///<disable LVD
/* Set the SLEEPDEEP bit to enable deep sleep mode (STOP) */
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
// If using KEIL's uVision, use the CMSIS intrinsic
__wfi();
}
当然,在进入休眠前,少不了要使能一些唤醒,否则就起不来了,所以在外层再封装一个函数,此处使能了GPIO以及CAN唤醒
void Mcu_GotoLowPower(void)
{
SPM->EN_PERIPH_WAKEUP |= SPM_EN_PERIPH_WAKEUP_CAN0_Msk | SPM_EN_PERIPH_WAKEUP_GPIO_Msk; ///<enable wakeup
sysStop();
}
三,can实现,对于can实现的具体就不细说了,对于一般的使用来说,can确实没有太多东西需要初始化的。在can.h文件中实现了如下接口。
typedef struct _can_msg
{
uint32_t ID;
uint32_t DLC:4; /*!< Data length code */
uint32_t FDF:1; /*!< FD format indicator */
uint32_t IDE:1; /*!< Identifier extension */
uint32_t reserved:26;
uint8_t DATA[64]; /*!< Data must 4bytes align*/
}CanMsgType;
extern void Can_Init(void);
extern void Can_DeInit(void);
extern int Can_Transmit(CanMsgType *canMsg);
extern int Can_Receive(CanMsgType *canMsg);
整体工程实现过程中遇到如下问题:
1,can模块DATA域必须4字节对齐
在cortex-m0+中是不支持非对齐访问的,而can模块的RBUF和TBUF又都是按照4字节访问,所以在访问时我们需要将我们定义的结构体中的DATA成员转为uint32,如果DATA未四字节对齐,将会导致非对齐访问错误而进入hardfault
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时就锁定了。
void Can_Init(void)
{
CKGEN->CTRL |= CKGEN_CTRL_CAN0_CLK_SEL_Msk;
CKGEN->PERI_CLK_EN_0 |= CKGEN_PERI_CLK_EN_0_CAN0_EN_Msk; ///<CAN clock en
CKGEN->PERI_SFT_RST0 |= CKGEN_PERI_SFT_RST0_SRST_CAN0_Msk;
CANx->CTRL0 |= CAN_CTRL0_RESET_Msk; ///<CAN set reset some register must write while RESET=1 and lock while RESET=0
CANx->CTRL0 |= CAN_CTRL0_FDISO_Msk; ///<CANFD
/*
tSeg1 = (S_SEG_1 + 2); tSeg2 = (S_SEG_2 + 1).
BandRate = (48M / (S_PRESC + 1) / ((S_SEG_1 + 2) + (S_SEG_2 + 1))) = 500K
SamplePoint = (tSeg1 / (tSeg1 + tSeg2)) = 81.25%.
*/
CANx->SBITRATE = (5 << CAN_BITRATE_PRESC_Pos) + (2 << CAN_BITRATE_SJW_Pos) + (2 << CAN_BITRATE_SEG_2_Pos) + 11; ///<Low speed
///<1M 81.25%
CANx->FBITRATE = (2 << CAN_BITRATE_PRESC_Pos) + (2 << CAN_BITRATE_SJW_Pos) + (2 << CAN_BITRATE_SEG_2_Pos) + 11; ///<High speed
CANx->CTRL0 &= ~CAN_CTRL0_RESET_Msk; ///<CAN reset
CANx->CTRL1 &= ~0xFE; ///<clear all interrupt enable
CANx->CTRL1 |= (CAN_CTRL1_EIE_Msk | CAN_CTRL1_RIE_Msk | CAN_CTRL1_TSIE_Msk | CAN_CTRL1_TPIE_Msk); ///<enable send receive and error interrupt
CANx->CTRL1 |= (15 << CAN_CTRL1_EWL_Pos); ///<EIF limit value set 128 ((EWL+1)*8)
NVIC_EnableIRQ(CAN0_IRQn);
}
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)
|