#include <stm32f10x.h>
#define _BV(n) (1 << (n))
uint8_t n = 0;
uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
void delay(void)
{
    uint32_t i;
    for (i = 0; i < 20000; i++);
}
void SerIn(uint8_t data)
{
    uint8_t i;
    for (i = 0; i < 8; i++)
    {
        GPIOC->BRR = _BV(15);
        if (data & 0x80)
            GPIOA->BSRR = _BV(0);
        else
            GPIOA->BRR = _BV(0);
        GPIOC->BSRR = _BV(15);
        data <<= 1;
    }
}
void ParOut(void)
{
    GPIOC->BRR = _BV(14);
    GPIOC->BSRR = _BV(14);
}
void seg_scan(uint8_t i)
{
    while (i--)
    {
        SerIn(seg8[n % 10]);
        SerIn(_BV(7));
        ParOut();
        delay();
        
        // 数码管只显示前两位变得慢的数值
        SerIn(seg8[TIM1->CNT % 10000 / 1000]);
        SerIn(_BV(1));
        ParOut();
        delay();
        
        SerIn(seg8[TIM1->CNT % 1000 / 100]);
        SerIn(_BV(0));
        ParOut();
        delay();
    }
}
int main(void)
{
    RCC->APB2ENR = _BV(2) | _BV(4) | _BV(11); // 开启PA、PC和TIM1时钟, 不需要开启AFIO的时钟
    GPIOA->CRH = 0x0000008b; // PA9设为输入, PA8设为复用50MHz推挽输出
    GPIOA->CRL = 0x00000003; // PA0设为50MHz推挽输出
    GPIOC->CRH = 0x33000000; // PC14~15设为50MHz推挽输出
    GPIOA->BSRR = _BV(9); // PA9输入带上拉, 即悬空时输入高电平
    TIM1->ARR = 5099;
    TIM1->PSC = 65535;
    
    TIM1->CR2 = 0x05; // CCUS=1(允许通过硬件触发COM事件), CCPC=1(只有触发COM事件后输出比较寄存器的设置才生效)
    TIM1->CCR1 = 2000; // 输出比较值
    TIM1->CCMR1 = 0xf160; // CC2S=01(IC2=>TI2, 通道2设为输入), IC2F=1111(硬件消抖), OC1M=110(preloaded, 通道1选PWM模式1)
    TIM1->BDTR = 0x8000; // MOE=1(允许通道输出)
    TIM1->CCER = 0x21; // CC2P=1(TI2下降沿触发), CC1E=1(preloaded, 开通道1输出)
    TIM1->SMCR = 0x60; // TS=110(设置TRGI为TI2(PA9), 作为COM事件触发端口)
    
    // 测试代码: 以下代码将定时器的时钟源设为外部信号TI2
    // 取消注释后才可触发TI中断, 数码管显示6
    //TIM1->SMCR |= 0x07; // SMS=111
    //TIM1->PSC = 0;
    
    // 开中断
    NVIC->ISER[0] = _BV(26);
    TIM1->DIER = 0x60; // COMIE=1, TIE=1
    
    // 刷新寄存器
    TIM1->CR1 = 0x54; // URS=1
    TIM1->EGR = 0x01; // UG=1
    
    // 开始计数(向上计数模式)
    TIM1->CR1 = 0x01; // CEN=1
    
    while (1)
    {
        seg_scan(1);
    }
}
void TIM1_TRG_COM_IRQHandler(void)
{
    if (TIM1->SR & _BV(5))
    {
        TIM1->SR &= ~_BV(5); // COMIF=0
        n = 5; // 触发COM中断时, 数码管最左端显示5
    }
    else if (TIM1->SR & _BV(6))
    {
        TIM1->SR &= ~_BV(6); // TIF=0
        n = 6; // 触发TI中断时, 数码管最左端显示6
    }
}
            
                      

