#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
}
}