引言:利用定时器TIM8产生1ms中断,每中断一次,全局变量+1,计数到10即10ms,使得输出引脚翻转一次。芯片采用STM32F103VCT6,系统输入时钟12MHz,完成代码并用示波器输出。
根据要求,本项目涉及系统时钟配置、中断源配置、GPIO输出配置、定时器配置四个部分。
1、准备工作
(1)系统时钟配置
? ? TIM8时钟为PLCK2,由APB2进行提供,时钟配置流程如下图(具体配置方法见STM32 RCC配置时钟 MCO输出实验_Artemis_yuer的博客-CSDN博客),本项目TIM8选择内部时钟CK_INT作为时钟源。
? ? ? ?配置步骤:
? ? ? ①12MHz外部时钟HSE输入;
? ? ? ②HSE分频器作为PLL输入,此处选择不分频;
? ? ? ③HSE时钟作为PLL输入时钟;
? ? ? ④PLL倍频系数,设置为6倍,产生72MHzPLLCLK时钟;
? ? ? ⑤选择PLLCLK作为系统时钟;
? ? ? ⑥设置AHB时钟,得到HCLK时钟;
? ? ? ⑦设置高速AHB时钟,通过APB2得到PLCK2时钟;
? ? ? ⑧PLCK2分频得到TIMx CLK;
? ? ? ⑨TIMx CLK作为TIM8内部时钟。
(2)中断源配置
? ? ? ? ?配置TIM8中断向量,这里直接用库函数进行配置。配置相应的主优先级和从优先级。
(3)GPIO输出配置
? ? ? ? ?TIM8位单片机的PC7引脚,因此需要使能GPIOC,即APB2 外设时钟使能寄存器(RCC_APB2ENR)第四位置1。
? ? ? ? PC7使用端口配置低寄存器(GPIOx_CRL)进行配置,设置为推挽输出模式,如图所示,寄存器复位值并不为0,首先要将寄存器第七位(即31:28)清零,然后赋值0011。
? ? ? ? ?本项目需要定时器每中断一次,全局变量+1,计数到10即10ms,使得输出引脚翻转一次,设置局部变量和中断输出函数,当进入十次中断后,局部变量+1,输出高电平,再进入十次中断后,局部变量再+1,输出低电平,以此来实现10ms的电平翻转。
? ? ? ?配置步骤:
? ? ? ?①IOPC使能;②PC7引脚清零;③PC7设置为推挽输出模式;④判断是否溢出中断;⑤清除中断标志位;⑥电平翻转输出。
(4)定时器配置
? ? ? ? 实验需设定定时器中断为1ms,输出方波为1kHz,定时中断周期计算公式:
? ? ? ? Tout =?((ARR+1)*(PSC+1))/Tclk ;
? ? ? ? 其中:Tclk:定时器的输入时钟频率(单位MHZ)
? ? ? ? Tout:定时器溢出时间(单位为us)
? ? ? ? PSC:定时器预分频值,将Tclk分频了PSC+1,定时器的最终频率就是Tclk/(PSC+1) MHz
? ? ? ? ARR:自动重装载寄存器,定时器计数的值被重装载一次被就是一个更新,若为向上计数,则从0开始重新计数。
? ? ? ? 这里为满足要求,设置PSC=72-1;即Tclk分频72次,得到1MHz定时器频率,为1us,项目需要中断为1ms,是1us的1000倍,则ARR值为1000-1,得到1ms中断值;f=1/T=1/1ms=1000Hz,也就是1kHZ的方波。而本项目需要产生20ms周期的方波,f=1/T=1/20ms=50Hz,则输出频率为50Hz的方波(若不需要20ms周期输出,则输出频率为1000Hz)。
? ? ? ?配置步骤:
? ? ?①TIM8时钟使能;
? ? ?②设置TIM8的PSC(预分频值)和ARR(自动重装溢出值);
? ? ?③输出使能(配置高级定时器的刹车和死区寄存器打开);
? ? ?④允许接收中断;
? ? ?⑤使能TIM8。
2、程序代码
(1)基于寄存器代码
#include "sys.h"
#include "main.h"
#include "stm32f10x.h"
#include "misc.h"
//时钟配置
void Stm32_Clock_Init()
{
uint8_t temp=0;
RCC_DeInit(); /* 时钟复位 */
RCC->CR|=1<<16; /* 使能HSE时钟 */
while(!(RCC->CR|=1>>17)) /* 判断HSE就绪 */
{
}
RCC->CFGR=0x00000400; /* APB1=DIV2;APB2=DIV1;AHB=DIV1 */
RCC->CFGR|=1<<16; /* 配置HSE时钟作为PLL时钟输入 */
FLASH->ACR|=0x32; /* FLASH延时两个周期 */
RCC->CFGR|=0x00100000; /* PLL 6倍频输出 */
RCC->CR|=1<<24; /* PLL使能 */
while(!(RCC->CR|=1>>25)) /* 判断PLL时钟就绪 */
{
}
RCC->CFGR|=0x02; /* 选择PLL为SYSCLK */
while(temp!=0x02) /* 判断SYSCLK是否设置成功 */
{
temp=RCC->CFGR>>2;
temp&=0x03;
}
}
//GPIO配置 PC7
void TIM8GPIOConfig()
{
RCC->APB2ENR|=1<<4; /* IOPC使能 */
GPIOC->CRL&=0X0FFFFFFF; /* PC7引脚清零 */
GPIOC->CRL|=0X30000000; /* PC7设置为推挽输出模式 */
}
//Timer配置 1kHz
void TIM8_Init()
{
RCC->APB2ENR|=1<<13; /* TIM8时钟使能 */
TIM8->ARR=999; /* 计数器自动重装值为1000,刚好1ms,1KHz */
TIM8->PSC=71; /* 预分频器72,得到1MHz的计数频率 */
TIM8->BDTR = 0x8000; /* 输出使能 */
TIM8->CR1|=0x0080; /* 寄存器装入缓冲器 */
TIM8->DIER|=1<<0; /* 允许更新中断 */
TIM8->DIER|=1<<6; /* 允许触发中断 */
TIM8->CR1 |=0X01; /* 使能定时器8 */
}
//TIM8中断优先级配置
void TIM8_IT_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//中断输出配置
uint16_t cnt=0;
void TIM8_UP_IRQHandler(void)
{
static uint16_t flag=0;
if(TIM8->SR&0x0001) /* 判断是否溢出中断 */
{
TIM8->SR&=~(1<<0); /* 清除中断标志位 */
cnt++; /* 每进一次中断,CNT+1 */
if(cnt>=10) /* 如果CNT=10,输出*/
{
cnt=0;
flag++; /* 每十个中断flag+1*/
if(flag%2==0) /* 若为奇数*/
{
GPIOC->ODR|=1<<7; /* 输出高电平*/
}
else
{
GPIOC->ODR&=0<<7; /* 否则输出低电平*/
}
}
}
}
int main()
{
Stm32_Clock_Init();
TIM8GPIOConfig();
TIM8_Init();
TIM8_IT_Init();
while(1)
{
}
}
(2)基于库函数代码
#include "sys.h"
#include "main.h"
#include "stm32f10x.h"
#include "misc.h"
uint16_t cnt=0;
void RCC_Init() /* 系统时钟配置 */
{
ErrorStatus HSEStartUpStatus; /* 枚举型变量 */
RCC_DeInit(); /* 重置RCC */
RCC_HSEConfig(RCC_HSE_ON); /* HSE使能 */
HSEStartUpStatus=RCC_WaitForHSEStartUp(); /* 等待HSE起振 */
if(HSEStartUpStatus==SUCCESS) /* 判断HSE起振成功 */
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
FLASH_SetLatency(FLASH_Latency_2); /* 2个延时周期*/
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2); /* 设置低速APB1时钟PLCK1*/
RCC_PCLK2Config(RCC_HCLK_Div1); /* 设置高速APB2时钟PLCK2*/
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_6); /* 6倍频 */
}
else
{
/*do nothing*/
}
RCC_PLLCmd(ENABLE); /* PLL使能 */
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET) /* 检查使能标志位 */
{
}
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); /* PLLCLK作为SYSCLK */
while (RCC_GetSYSCLKSource()!=0x08)
/* 判断SYSCLK是否设置成功 0x08PLL作为系统时钟 */
{
}
}
void TIM8_GPIO_Config(void) /* GPIO配置 */
{
GPIO_InitTypeDef GPIO_InitStruct; /* 结构体定义 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); /* GPIOC使能 */
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_7; /* PC7引脚 */
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP; /* 推挽输出 */
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; /* 50MHz*/
GPIO_Init(GPIOC,&GPIO_InitStruct); /*初始化GPIOC */
}
void TIM8_Init()
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); /* TIM8时钟使能 */
TIM_DeInit(TIM8);
TIM_InternalClockConfig(TIM8);
TIM_TimeBaseStructure.TIM_Period = 71; /* 预分频器72,得到1MHz的计数频率 */
TIM_TimeBaseStructure.TIM_Prescaler =999; /* 计数器自动重装值为1000 */
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; /* 向上计数 */
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM8,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM8, ENABLE);
}
void TIM8_IT_Init()
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM8_UP_IRQHandler(void)
{
static uint16_t flag=0;
if (TIM_GetITStatus(TIM8, TIM_IT_Update)!= RESET)
{
TIM_ClearITPendingBit(TIM8, TIM_IT_Update);
cnt++;
if(cnt>=100)
{
cnt=0;
flag++;
if(flag%2==0)
{
GPIO_SetBits(GPIOC,GPIO_Pin_7);
}
else
{
GPIO_ResetBits(GPIOC,GPIO_Pin_7);
}
}
}
}
int main()
{
RCC_Init();
TIM8_GPIO_Config();
TIM8_Init();
TIM8_IT_Init();
while(1)
{
}
}
3、信号输出
生成代码编译后烧写至芯片内,通过示波器显示如下图:
???? ? ? ? ? ?
? ? ? ?由示波器可以观察到输出周期为20ms,也就是说配置定时器中断为1ms,,示波器频率显示为50HZ,实验成功。
|