我是一个stm32芯片的新手,以前长使用的芯片是AVR的。
学习stm32芯片,就是从原子兄的《不完全手册》开始的,最近做了一个产品,却遇到了一个头疼的问题。
我做的一个产品,平常是休眠(stop模式)的,用户需要使用的时候,就通过一个键盘唤醒系统,唤醒后大概8秒钟未见用户操作的话,系统就再次进入休眠程序。
用户在开始使用我的这个产品的时候,一切正常,但用了大约个把月后,就出现了问题,系统无法唤醒了。程序如下:
void fIntoSleep(void)
{
fIoIntoSleep();
fTimerIntoSleep();
fIntIntoSleep();
fDmaIntoSleep();
fDacIntoSleep();
fUsartIntoSleep();
fAdcIntoSleep();
sSys.bSysStatus = 0;
DBGMCU->CR &= ~ (1 << 2); //非调试模式下,standby模式时关闭JTAG接口运行以降低功耗
DBGMCU->CR &= ~ (1 << 1); //非调试模式下,stop模式时关闭JTAG接口运行以降低功耗
RCC->CR &= ~ (1 << 0); //非调试模式下关闭内部RC振荡以降低功耗
// fSysStandby();
fSysStop();
//-------------复位------------------
fCloseIRQHandler();
fSoftReset();
}
void fIoIntoSleep(void)
{
//所有IO被配置为模拟输入(省电)
//PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7 PA8 PA9 PA10 PA11 PA12 PA13 PA14 PA15
//I I I I I I I I I I I O O I I I
//A A A A A A A A A A A 0 0 A A A
GPIOA->ODR = 0x00000000;
GPIOA->CRL = 0x00000000;
GPIOA->CRH = 0x00022220;
//PB0 PB1 PB2 PB3 PB4 PB5 PB6 PB7 PB8 PB9 PB10 PB11 PB12 PB13 PB14 PB15
//I I I I O I I I I I I I O O O O
//A A T A 0 P T P P P P P 0 0 0 0
GPIOB->ODR = 0x00000fa0;
GPIOB->CRL = 0x84820400;
GPIOB->CRH = 0x22228888;
//PC0 PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8 PC9 PC10 PC11 PC12 PC13 PC14 PC15
//I I I I I I I I I I I I I I I I
//A A A A A A A A A A A A A A A A
GPIOC->ODR = 0x00000000;
GPIOC->CRL = 0x00000000;
GPIOC->CRH = 0x00000000;
/* RCC->APB2ENR &= ~ (1 << 2); //portA时钟关闭
RCC->APB2ENR &= ~ (1 << 3); //portB时钟关闭
RCC->APB2ENR &= ~ (1 << 4); //portC时钟关闭
RCC->APB2ENR &= ~ (1 << 5); //portD时钟关闭
RCC->APB2ENR &= ~ (1 << 0); //复用功能时钟关闭*/
}
void fTimerIntoSleep(void)
{
TIM3->CR1 &= ~ (1 << 0); //定时器3关闭
RCC->APB1RSTR |= 1 << 1;//TIM3复位
RCC->APB1RSTR &= ~(1 << 1);//复位结束
RCC->APB1ENR &= ~ (1 << 1);//TIM3 时钟关闭
TIM6->CR1 &= ~ (1 << 0);//TIM6关闭
RCC->APB1RSTR |= 1 << 4;//TIM6复位
RCC->APB1RSTR &= ~(1 << 4);//复位结束
RCC->APB1ENR &= ~ (1 << 4);//TIM6时钟关闭
}
void fIntIntoSleep(void)
{
RCC->APB2ENR |= 1 << 3; //使能PORTB 时钟
// JTAG_Set(JTAG_SWD_DISABLE);//关闭JTAG 和SWD
GPIOB->ODR &= 0xffff001b;
GPIOB->ODR |= 0x00000fa0;
GPIOB->CRL &= 0x000ff0ff;
GPIOB->CRL |= 0x84800400;
GPIOB->CRH &= 0x00000000;
GPIOB->CRH |= 0x22228888;//设置PB2(gSlide)高阻输入、PB5(gLineCtrl)上拉输入、PB6(gDobLock)高阻输入、PB7(gMechKey)上拉输入、PB8(gLatchBolt)上拉输入、PB9、PB10、PB11(Key0、Key1、Key2)上拉输入、PB12、PB13、PB14、PB15(Key3、Key4、Key5、Key6)输出0
EXTI->FTSR = 0;
EXTI->FTSR |= 0x00000fe4;//PB2、PB5-11下降沿触发
EXTI->RTSR = 0;
EXTI->RTSR |= 0x00000044;//PB2、PB6上升沿触发
EXTI->IMR = 0;
AFIO->EXTICR[0] = 0x00000100;
AFIO->EXTICR[1] = 0x00001110;
AFIO->EXTICR[2] = 0x00001111;
EXTI->IMR |= 0x00000fa4;//PB2、PB5-11沿中断打开(PB6反锁功能,不用打开中断)
fNvicSet(3,3,EXTI0_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI1_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI2_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI3_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI4_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI9_5_IRQChannel);//组3,最低优先级
fNvicSet(3,3,EXTI15_10_IRQChannel);//组3,最低优先级
}
void EXTI0_IRQHandler(void)
{
EXTI-> R |= 1 << 0; //清除LINE0 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void EXTI1_IRQHandler(void)
{
EXTI-> R |= 1 << 1; //清除LINE1 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void EXTI2_IRQHandler(void)
{
EXTI-> R |= 1 << 2; //清除LINE2 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void EXTI4_IRQHandler(void)
{
EXTI-> R |= 1 << 4; //清除LINE4 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void EXTI9_5_IRQHandler(void)
{
EXTI-> R |= 0x0f << 6; //清除LINE6-9 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void EXTI15_10_IRQHandler(void)
{
EXTI-> R |= 0x1f << 10; //清除LINE13 上的中断标志位
// EXTI->FTSR = 0;
// EXTI->RTSR = 0;
// AFIO->EXTICR[0] = 0;
// AFIO->EXTICR[1] = 0;
// AFIO->EXTICR[2] = 0;
// AFIO->EXTICR[3] = 0;
// EXTI->IMR = 0;
}
void fCloseIRQHandler(void)
{
EXTI->FTSR = 0;
EXTI->RTSR = 0;
AFIO->EXTICR[0] = 0;
AFIO->EXTICR[1] = 0;
AFIO->EXTICR[2] = 0;
AFIO->EXTICR[3] = 0;
EXTI->IMR = 0;
}
void fDmaIntoSleep(void)
{
DMA1_Channel3->CCR &= ~(1 << 0); //关闭DMA 传输
RCC->AHBENR &= ~ (1 << 0);//Dma1 时钟关闭
}
void fDacIntoSleep(void)
{
DAC->CR &= ~ (1 << 0);
RCC->APB1RSTR |= 1 << 29;//复位DAC接口
RCC->APB1RSTR &= ~(1 << 29);//复位结束
RCC->APB1ENR &= ~ (1 << 29);//DAC接口时钟关闭
}
void fUsartIntoSleep(void)
{
RCC->APB2RSTR |= 1 << 14; //复位串口1
RCC->APB2RSTR &= ~ (1 << 14);//停止复位
RCC->APB2ENR &= ~ (1 << 14); //串口1时钟关闭
RCC->APB1RSTR |= 1 << 17; //复位串口2
RCC->APB1RSTR &= ~ (1 << 17);//停止复位
RCC->APB1ENR &= ~ (1 << 17); //串口2时钟关闭
}
void fAdcIntoSleep(void)
{
ADC1->CR2 &= ~ (1 << 0);
RCC->APB2RSTR |= 1 << 9; //ADC1 复位
RCC->APB2RSTR &= ~ (1 << 9);//复位结束
RCC->APB2ENR &= ~ (1 << 9); //ADC1 时钟关闭
}
//进入待机模式
void fSysStandby(void)
{
SCB->SCR |= 1 << 2;//使能SLEEPDEEP位 (SYS->CTRL)
RCC->APB1ENR |= 1 << 28; //使能电源时钟
  WR->CSR |= 1 << 8; //设置WKUP用于唤醒
  WR->CR |= 1 << 2; //清除Wake-up 标志
  WR->CR |= 1 << 1; //PDDS置位
__wfi(); //执行WFI指令
}
void fSysStop(void)
{
SCB->SCR |= 1 << 2;//使能SLEEPDEEP位 (SYS->CTRL)
RCC->APB1ENR |= 1 << 28;//使能电源时钟
  WR->CR |= 1 << 2; //清除唤醒位
PWR->CR |= 1 << 0; //停机模式下电压调节器处于低功耗模式
PWR->CR &= ~ (1 << 1); //当CPU进入休眠时,进入停机模式,电压调节器由LPDS位控制
__wfi();
}
//系统软复位
void fSoftReset(void)
{
SCB->AIRCR = 0x05FA0000 | (DWORD)0x04;
}
//设置每个中断的NVIC,由每个中断初始化函数调用
//bPriorty_NvicInit:抢占优先级
//bSubPriority_NvicInit:响应优先级
//bChannel_NvicInit:中断编号
//注意优先级不能超过设定的组的范围!否则会有意想不到的错误
//组划分:
//组0:0位抢占优先级,4位响应优先级
//组1:1位抢占优先级,3位响应优先级
//组2:2位抢占优先级,2位响应优先级
//组3:3位抢占优先级,1位响应优先级
//组4:4位抢占优先级,0位响应优先级
//bPriorty_NvicSet和bSubPriority_NvicSe的原则是,数值越小,越优先
//各中断抢占优先级设置根据
//0级:需要尽量实时,且一旦延时,会影响效果的中断,且处理过程特别简单
//1级:需要在较小的一段时间(10us内)内作出处理的中断
//2级:可以在一段时间(100us)内作出处理的中断
//3级:可以在较长时间(10ms)内作出处理的中断
void fNvicSet(BYTE bPriorty_fNvicSet,BYTE bSubPriority_fNvicSet,BYTE bChannel_fNvicSet)
{
DWORD dTemp_fNvicSet;
BYTE bIprAdr_fNvicSet = bChannel_fNvicSet / 4; //每组只能存4个,得到组地址
BYTE bIprOft_fNvicSet = bChannel_fNvicSet % 4;//在组内的偏移
bIprOft_fNvicSet = bIprOft_fNvicSet * 8 + 4; //得到偏移的确切位置
dTemp_fNvicSet = bPriorty_fNvicSet << (4 - mGroupPriorityInit);
dTemp_fNvicSet |= bSubPriority_fNvicSet & (0x0f >> mGroupPriorityInit);
dTemp_fNvicSet &= 0xf;//取低四位
if(bChannel_fNvicSet < 32)
{
NVIC->ISER[0] |= 1 << bChannel_fNvicSet;//使能中断位(要清除的话,相反操作就OK)
}
else
{
NVIC->ISER[1] |= 1 << (bChannel_fNvicSet - 32);
}
NVIC->IPR[bIprAdr_fNvicSet] |= dTemp_fNvicSet << bIprOft_fNvicSet;//设置响应优先级和抢断优先级
}
另外说明一下,我的中断采用4x4的格式,即4个抢占级,每个抢占级中分4级,堆栈长度是0x00000200。在主程序中(未贴出来)使用的cpu资源如下:usart1(中断模式)、usart2(中断模式)、tim3(中断模式)、tim6(作为dma的时钟)、RTC计数器开启(未开启中断)、ADC开启(未开启中断),主程序中未开启任何外部中断、DMA1(中断模式)、spi1口(扫描模式,未开启中断)、DAC(与DMA组合使用,未开启DAC中断)、systick计数器开启(未开启中断)。
希望能得到原子兄及各位的指点,谢谢大家!
|