#include <stm32f10x.h>
uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
uint16_t num = 0;
void delay(void)
{
uint16_t i;
for (i = 0; i < 20000; i++);
}
void ser_in(uint8_t data)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
GPIOB->BRR = GPIO_BRR_BR9; // SCLK=PB9
if (data & 0x80)
GPIOB->BSRR = GPIO_BSRR_BS7; // DIO=PB7
else
GPIOB->BRR = GPIO_BRR_BR7;
data <<= 1;
GPIOB->BSRR = GPIO_BSRR_BS9;
}
}
void par_out(void)
{
GPIOB->BRR = GPIO_BRR_BR8; // RCLK=PB8
GPIOB->BSRR = GPIO_BSRR_BS8;
}
void seg_scan(void)
{
uint8_t i;
uint16_t numbuf = num;
for (i = 0; i <= 4; i++)
{
ser_in(seg8[numbuf % 10]);
ser_in(1 << i);
par_out();
delay();
numbuf /= 10;
}
}
void seg_off(void)
{
ser_in(0xff);
ser_in(0x00);
par_out();
}
int main(void)
{
uint8_t i;
RCC->APB1ENR = RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN;
PWR->CR |= PWR_CR_DBP; // 允许写入后备寄存器
// 上电复位: PORRSTF=PINRSTF=1
// 复位键复位: PINRSTF=1
if (RCC->CSR & RCC_CSR_PORRSTF)
BKP->DR1 = num = 1970; // 通电时写入初值
else
num = BKP->DR1; // 复位时读取寄存器值
if (RCC->CSR & RCC_CSR_IWDGRSTF)
BKP->DR1 = --num; // 如果是看门狗复位, 则减去1
RCC->CSR |= RCC_CSR_RMVF; // 清除复位标志信息
/* 配置GPIO口*/
RCC->APB2ENR = RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
GPIOB->CRL = 0x30000000;
GPIOB->CRH = 0x00000033;
GPIOC->CRL = 0x00000080; // PC1, PC13设为带电阻输入
GPIOC->CRH = 0x00800000;
GPIOC->BSRR = GPIO_BSRR_BS1 | GPIO_BSRR_BS13; // PC1, PC13带上拉电阻输入
// 普通睡眠模式下可以用定时器中断来喂狗
// 停止模式和待机模式下只能通过RTC闹钟中断来喂狗, 因为定时器在这两个模式下不可用
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN;
TIM6->ARR = 9499; // 0.95s喂一次狗
TIM6->PSC = 7199;
TIM6->DIER = TIM_DIER_UIE;
TIM6->CR1 = TIM_CR1_URS;
TIM6->EGR = TIM_EGR_UG;
NVIC_EnableIRQ(TIM6_IRQn);
// 对于待机模式, 反正唤醒后RAM数据要丢失
// 不如在待机之前先通过软件将系统复位一次, 将看门狗关闭
// 复位后判断是否待机的标志可以保存在备用寄存器里面
// 配置外部中断
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
AFIO->EXTICR[0] = AFIO_EXTICR1_EXTI1_PC;
AFIO->EXTICR[3] = AFIO_EXTICR4_EXTI13_PC;
EXTI->IMR |= EXTI_IMR_MR1 | EXTI_IMR_MR13;
EXTI->FTSR |= EXTI_FTSR_TR1 | EXTI_FTSR_TR13;
NVIC_EnableIRQ(EXTI1_IRQn);
NVIC_EnableIRQ(EXTI15_10_IRQn);
// 配置独立看门狗
IWDG->KR = 0x5555; // 开始配置
IWDG->PR = 3; // 32分频, f=40kHz/32=1250Hz->0.8ms
IWDG->RLR = 1250; // 定时时间: 1250*0.8ms=1s, 1250=0x4e2
// 写入后不需要等待PVU, RVU标志位清零
IWDG->KR = 0xcccc; // 启动看门狗
IWDG->KR = 0xaaaa; // 导入上述配置
while (1)
{
for (i = 0; i < 20; i++)
seg_scan();
IWDG->KR = 0xaaaa; // 非睡眠模式时在这里喂狗
}
}
// 按键PC1: 进入睡眠模式
void EXTI1_IRQHandler(void)
{
EXTI->PR |= EXTI_PR_PR1;
seg_off(); // 熄灭数码管
TIM6->CR1 |= TIM_CR1_CEN; // 开定时器6
IWDG->KR = 0xaaaa; // 先喂一次狗
SCB->SCR |= SCB_SCR_SLEEPONEXIT; // 退出中断时自动执行_WFI()
}
// 按键PC13: 退出睡眠模式
void EXTI15_10_IRQHandler(void)
{
EXTI->PR |= EXTI_PR_PR13;
TIM6->CR1 &= ~TIM_CR1_CEN; // 关定时器6, 停止用中断喂狗
SCB->SCR &= ~SCB_SCR_SLEEPONEXIT; // 退出这个中断时不进入睡眠模式
}
// 睡眠模式时用于喂狗的中断
void TIM6_IRQHandler(void)
{
TIM6->SR &= ~TIM_SR_UIF;
IWDG->KR = 0xaaaa;
// 由于SLEEPONEXIT=1, 因此退出这个中断函数时仍会自动进入睡眠模式
}