记录STM32中的配置
一、DMA
1. 各个外设使用的通道
2.配置参数
地址&来源
缓存大小
外设地址寄存器递增(通道接入多个外设)、内存地址寄存器递增(接入多块内存)
通常外设会被Disable,内存是Enable
设定外设数据宽度、内存数据宽度
工作模式
软件优先级
内存到内存
3. CODE EXAMPLE
配置DMA的函数,打开从内存到USARTy、USARTz的TX寄存器的DMA发送通道
- 地址&来源:
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
- 缓存大小
DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
即为发送的字符串长度 - 增长:
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable
- 数据宽度:一个字节
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
- 模式&优先级&内存到内存
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
- 根据不同的外设查表选通道,并且使能这个通道
DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
DMA_Cmd(USARTy_Tx_DMA_Channel, ENABLE);
汇总为一个Configuration的函数:
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
DMA_DeInit(USARTy_Tx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
/* USARTz_Tx_DMA_Channel (triggered by USARTz Tx event) Config */
DMA_DeInit(USARTz_Tx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTz_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TxBufferSize2;
DMA_Init(USARTz_Tx_DMA_Channel, &DMA_InitStructure);
/* Enable USARTy DMA TX Channel */
DMA_Cmd(USARTy_Tx_DMA_Channel, ENABLE);
/* Enable USARTz DMA TX Channel */
DMA_Cmd(USARTz_Tx_DMA_Channel, ENABLE);
}
注意:DMA一边打开了通道,外设一边也同样需要打开通道才能进行DMA传输
/* Enable USARTy DMA TX request */
USART_DMACmd(USARTy, USART_DMAReq_Tx, ENABLE);
为什么要这样层层瞎几把起名字。。。。
二、USART
1.配置
- 基本属性配置:波特率,字长,停止位,校验位,控制流,输入or输出
- 初始化具体的USART
- 使能UASART
void USART_Configuration(void)
{
/* USARTy and USARTz configured as follow:
- BaudRate = 230400 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled */
USART_InitStructure.USART_BaudRate = 230400;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* Configure USARTz */
USART_Init(USARTz, &USART_InitStructure);
/* Enable the USARTz Receive Interrupt */
USART_ITConfig(USARTz, USART_IT_RXNE, ENABLE);
/* Enable USARTz */
USART_Cmd(USARTz, ENABLE);
}
配置完后,若TX内有数据,就会直接发出。需要通过导线将TX引脚与RX引脚接在一起。
2.接收处理
中断的接收方式:
我们先前以及运行接收器发出中断:USART_ITConfig(USARTz, USART_IT_RXNE, ENABLE);
RXNE即为RX not empty,也就是RX接收到数据,立即发出中断请求。
先设置
uint8_t RxCounter = 0;
extern uint8_t RxBuffer2[]; # 引用main中设置的缓存区间
extern uint8_t NbrOfDataToRead; # NumberOfDataToRead
再找到void USART3_IRQHandler(void)进行编写
- 读取数据,先进行
USART_GetITStatus(USART3, USART_IT_RXNE) != RESET
判断以确定有数据可以读入。再使用RxBuffer2[RxCounter++] = USART_ReceiveData(USART3);
读入一个单位的数据。 - 关闭中断:如果
RxCounter == NbrOfDataToRead
,则读完了全部的数据,那么USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
关闭这个中断
/**
* @brief This function handles USART3 global interrupt request.
* @param None
* @retval None
*/
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
/* Read one byte from the receive data register */
RxBuffer2[RxCounter++] = USART_ReceiveData(USART3);
if(RxCounter == NbrOfDataToRead)
{
/* Disable the USART3 Receive interrupt */
USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
}
}
}
三、NVIC
1.配置:
记录了各个中断请求编号
为了打开USART2、USART3的中断请求,选择出USART2_IRQn, USART3_IRQn
再配置
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the USARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USARTz_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
2.外设允许中断:
NVIC只是一个控制器。虽然配置完了NVIC,但是还是需要在响应的外设中打开中断和确定具体的中断方式。
USART_ITConfig(USARTz, USART_IT_RXNE, ENABLE);
以上代表USARTz会发出接收器非空的中断。
3.编写中断函数
找到void USART3_IRQHandler(void)
进行函数功能编写.
中断函数内部首先进行中断类型判断,再分别执行不同的功能。
例如:if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
判断是否是接收器非空的中断响应。
四、GPIO
1. 输入输出方式:
(1) GPIO_Mode_AIN 模拟输入
(2) GPIO_Mode_IN_FLOATING 浮空输入
(3) GPIO_Mode_IPD 下拉输入
(4) GPIO_Mode_IPU 上拉输入
(5) GPIO_Mode_Out_OD 开漏输出
(6) GPIO_Mode_Out_PP 推挽输出
(7) GPIO_Mode_AF_OD 复用开漏输出
(8) GPIO_Mode_AF_PP 复用推挽输出
①上拉输入、下拉输入可以用来检测外部信号;例如,按键等;
②浮空输入模式,由于输入阻抗较大,一般把这种模式用于标准通信协议的I2C、USART的接收端;
③普通推挽输出模式一般应用在输出电平为0和3.3V的场合。而普通开漏输出模式一般应用在电平不匹配的场合,如需要输出5V的高电平,就需要在外部一个上拉电阻,电源为5V,把GPIO设置为开漏模式,当输出高阻态时,由上拉电阻和电源向外输出5V电平。
④对于相应的复用模式(复用输出来源片上外设),则是根据GPIO的复用功能来选择,如GPIO的引脚用作串口的输出(USART/SPI/CAN),则使用复用推挽输出模式。如果用在I2C、SMBUS这些需要线与功能的复用场合,就使用复用开漏模式。
⑤在使用任何一种开漏模式时,都需要接上拉电阻。
2.配置
- 选定管脚:
GPIO_InitStructure.GPIO_Pin = USARTy_TxPin;
- 选定输入输出方式:
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- 选定频率:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- 选定端口号并初始化:
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable the USART3 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);
/* Enable the USART2 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
/* Configure USARTy Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTy_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTz_RxPin;
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
/* Configure USARTy Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTy_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTz_TxPin;
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
}
五、ADC
1. 资源
(a) STM32VC107一共有三个ADC
(b) 每个ADC是12位,十进制为0-4095,十六进制为0x000-0xFFF。
如果我们要转换的电压范围是0v-3.3v的话,转换器就会把0v-3.3v平均分成4096份。设转换器所得到的值为x,所求电压值为y。y=x/4096。
© 16个外部通道:简单的说就是芯片上有16个引脚是可以接到模拟电压上进行电压值检测的。16个通道不是独立的分配给ADC1、ADC2、ADC3使用,有些通道是被多个转换器共用的。
除此之外,还有俩内部通道,分别接到温度传感器与内部参考电压上。
(d) 连到PC4 的10K 欧姆的电位器RV1
2. ADC模式
3、栗子
1. 三通道+DMA(copy)
配置DMA:
ADCx将转换结果放在:ADCx->DR内,因此
/*基于DMA的ADC多通道采集*/
volatile uint16 ADCConvertedValue[10][3];
//用来存放ADC转换结果,也是DMA的目标地址,3通道,每通道采集10次后面取平均数
//ADCConvertedValue的定义用了volatile修饰词,因为这样可以保证每次的读取都是从绝对地址读出来的值,不会因为被会编译器进行优化导致读取到的值不是实时的AD值。
void DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能时钟
DMA_DeInit(DMA1_Channel1); //将通道一寄存器设为默认值
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);//该参数用以定义DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;//该参数用以定义DMA内存基地址(转换结果保存的地址)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
DMA_InitStructure.DMA_BufferSize = 3*10;//定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADCConvertedValue的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设定外设地址寄存器递增与否,此处设为不变 Disable
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//用来设定内存地址寄存器递增与否,此处设为递增,Enable
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//使能DMA通道的内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//根据DMA_InitStruct中指定的参数初始化DMA的通道
DMA_Cmd(DMA1_Channel1, ENABLE);//启动DMA通道一
}
配置ADC:
在确定完所选择的通道,以及其对应的管脚后,ADC配置通常分为5步骤:
- 初始化选定的GPIO管脚
- 初始化ADC(模式,通道数目)
- 为ADC配置通道,确定各个通道的扫描先后顺序
- 开启
- 校准等待
注释:
- 通过查询引脚表,可知PA0,PA1,PA2分别对应于ADC通道0,1,2。因此初始化这些GPIO管脚。
- 通常RCC_APB2PeriphClockCmd给ADC接入72MHz高速时钟。
- ADC的采样时钟最快14MHz。用RCC_ADCCLKConfig(RCC_PCLK2_Div6)进行分频:72M/6=12。
- ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
在多通道转换的时候,扫描模式与连续转换需要被ENABLE。单通道则DISABLE。
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/*3个IO口的配置(PA0、PA1、PA2)*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*IO和ADC使能时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 3;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);//通道一转换结果保存到ADCConvertedValue[0~10][0]
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_239Cycles5););//通道二转换结果保存到ADCConvertedValue[0~10][1]
ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_239Cycles5); );//通道三转换结果保存到ADCConvertedValue[0~10][2]
ADC_DMACmd(ADC1, ENABLE);//开启ADC的DMA支持
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
注释:
- 由于选择了软件触发,所以ADC转换开启不是自动进行的,而是通过函数
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
进行触发的
int main(void)
{
int sum;
u8 i,j;
float ADC_Value[3];//用来保存经过转换得到的电压值
ADC_Init();
DMA_Init();
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//开始采集
while(1)
{
for(i=0;i<3;i<++)
{
sum=0;
for(j=0;j<10;j++)
{
sum+=ADCConvertedValue[j][i];
}
ADC_Value[i]=(float)sum/(10*4096)*3.3;//求平均值并转换成电压值
//打印(略)
}
//延时(略)
}
}
2.WatchDog
- 超出阈值范围,发生中断ADC_IT_AWD。AWD:analog watch dog.
/* Configure high and low analog watchdog thresholds */
ADC_AnalogWatchdogThresholdsConfig(ADC1, 0x0B00, 0x0300);
/* Configure channel14 as the single analog watchdog guarded channel */
ADC_AnalogWatchdogSingleChannelConfig(ADC1, ADC_Channel_14);
/* Enable analog watchdog on one regular channel */
ADC_AnalogWatchdogCmd(ADC1, ADC_AnalogWatchdog_SingleRegEnable);
/* Enable AWD interupt */
ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE);
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Configure and enable ADC interrupt */
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void ADC1_2_IRQHandler(void)
{
/* Toggle LED1 */
STM_EVAL_LEDOn(LED1);
printf("interrupt occur\r");
STM_EVAL_LEDOff(LED1);
printf(" \r");
/* Clear ADC1 AWD pending interrupt bit */
ADC_ClearITPendingBit(ADC1, ADC_IT_AWD);
}
六、时钟
系统时钟
1.HSE高速外部时钟(常用8MHz无源晶振);
2.PLL时钟源(来源有HSE和HSI/2,一般选HSE作为时钟来源);
3.PLL时钟PLLCLK(通过设置PLL的倍频因子,一般8Mx9=72MHz,72MHz是官方推荐稳定运行时钟,最高128MHz);
4.系统时钟SYSCLK(一般SYSCLK=PLLCLK=72MHz);
5.AHB总线时钟HCLK(是系统时钟SYSCLK经过AHB分频器分频后得到的时钟,也就是APB总线时钟,一般设置1分频,HCLK=SYSSCLK=72MHz);
6.APB2总线时钟HCLK2(APB2总线时钟PCLK2由 HCLK经过高速APB2预分频器得到,分频因子可以是:[1,2,4,8,16],具体由时钟配置寄存器CFGR的位13-11:PPRE2[2:0]决定,一般设置为 1 分频,即 PCLK2 = HCLK =72M);
7.APB1总线时钟HCLK1(APB1 总线时钟 PCLK1 由 HCLK 经过低速 APB 预分频器得到,HCLK1 属于低速的总线时钟,最高为 36M,这里只需粗线条的设置好 APB1 的时钟即可。我们这里设置为 2分频,即 PCLK1 = HCLK/2 = 36M)设置系统时钟函数在库函数system_stm32f10x.c
设置
- 设置系统时钟SYSCLK、
- 设置AHB分频因子(决定HCLK是多少)、
- 设置APB2分频因子(设定PCLK2等于多少)、
设置APB1分频因子(决定PCLK1等于多少)、
控制AHB/APB2/APB1这3条总线开启,控制每个外设时钟的开启。
对于SYSCLK、HCLK、PCLK2、PCLK1这4个时钟的配置一般是:
PCLK2=HCLK=SYSCLK=PLLCLK=72MHz,PCLK1=HCLK/2=36MHz.这个配置是库函数的标准配置。
注意:除了RCC_APB2PeriphClockCmd还有RCC_APB1PeriphClockCmd,那么该如何选择?
APB2:高速时钟,最高72MHz,主要负责AD输入,I/O,串口1,高级定时器TIM
APB1:低速时钟,最高36MHz,主要负责DA输出,串口2、3、4、5,普通定时器TIM,USB,IIC,CAN,SPI
/* RCC system reset(for debug purpose) */
RCC_DeInit();
/* Enable HSE */
RCC_HSEConfig(RCC_HSE_ON);
/* Wait till HSE is ready */
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
/* Enable Prefetch Buffer */
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
/* Flash 2 wait state */
FLASH_SetLatency(FLASH_Latency_2);
/* HCLK = SYSCLK */
RCC_HCLKConfig(RCC_SYSCLK_Div1);
/* PCLK2 = HCLK */
RCC_PCLK2Config(RCC_HCLK_Div1);
/* PCLK1 = HCLK/2 */
RCC_PCLK1Config(RCC_HCLK_Div2);
/* ADCCLK = PCLK2/4 */
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
#ifndef STM32F10X_CL
/* PLLCLK = 8MHz * 7 = 56 MHz */
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_7);
#else
/* Configure PLLs *********************************************************/
/* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz */
RCC_PREDIV2Config(RCC_PREDIV2_Div5);
RCC_PLL2Config(RCC_PLL2Mul_8);
/* Enable PLL2 */
RCC_PLL2Cmd(ENABLE);
/* Wait till PLL2 is ready */
while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET)
{}
/* PLL configuration: PLLCLK = (PLL2 / 5) * 7 = 56 MHz */
RCC_PREDIV1Config(RCC_PREDIV1_Source_PLL2, RCC_PREDIV1_Div5);
RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);
#endif
/* Enable PLL */
RCC_PLLCmd(ENABLE);
/* Wait till PLL is ready */
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
{
}
/* Select PLL as system clock source */
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
/* Wait till PLL is used as system clock source */
while(RCC_GetSYSCLKSource() != 0x08)
{
}
/* Enable peripheral clocks --------------------------------------------------*/
...
}
void RCC_Configuration(void)
{
/* Setup the microcontroller system. Initialize the Embedded Flash Interface,
initialize the PLL and update the SystemFrequency variable. */
SystemInit();
/* Enable TIM2, TIM3 and TIM4 clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 |
RCC_APB1Periph_TIM4, ENABLE);
}
七、TIM
1.资源
STM32F1 系列共有八个TIM,分为基本定时器,通用定时器和高级定时器。
基本定时器 TIM6/7是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
输入捕捉和输出比较是共用相同的IO引脚,所以名称标注是一模一样。也就是说,每个通用定时器都只有四个独立通道,当某一通道作为了输入触发功能那就不能再作为输出匹配功能。
中断标记如下:
TIM_IT_Update: TIM update Interrupt source
TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
TIM_IT_COM: TIM Commutation Interrupt source
TIM_IT_Trigger: TIM Trigger Interrupt source
TIM_IT_Break: TIM Break Interrupt source
可以看出输入捕捉,输出比较是共用一种中断标记的。
1、时钟源
定时器时钟TIMxCLK,即内部时钟CK_INT,经APB1预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M 。
2、计数器时钟
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。
具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
3.计数器
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
4、自动重装载寄存器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
2. 定时时间的计算
定时器的定时时间等于计数器的中断周期乘以中断的次数。
计数时钟:CK_CNT = TIMxCLK/(PSC+1) MHz
计一个数的时间则是 CK_CLK 的倒数:T = 1/CK_CNT s
计数满ARR后产生一次中断,经历的时间则等于:interupt_T = T * (ARR+1) s
若变量 times记录了中断的次数,定时时间等于: interupt_T * times
3.基本配置
typedef struct
{
uint16_t TIM_Prescaler; /* 预分频器的值,时钟源的时钟经过该预分频后产生计数时钟CNT_CLK,存储在TIMx_PSC ,0x0000-0xFFFF (0-65535)*/
uint16_t TIM_CounterMode; /*定时器计数方式,可是在为向上计数、向下计数以及三种中心对齐模式。基本定时器只能是向上计数,即 TIMx_CNT只能从 0开始递增,并且无需初始化。 */
uint16_t TIM_Period; /*定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为 0至 65535。 */
uint16_t TIM_ClockDivision; /*时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分频比,基本定时器没有此功能,不用设置。*/
uint8_t TIM_RepetitionCounter; /*设置重复溢出次数,就是多少次溢出后会给你一次中断,一般设置为0,只有高级定时器才有用;*/
} TIM_TimeBaseInitTypeDef;
例如:
void Timer2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_DeInit(TIM2);//使用缺省值初始化TIM外设寄存器
TIM_TimeBaseStructure.TIM_Period=1;//自动重装载寄存器值为1
TIM_TimeBaseStructure.TIM_Prescaler=(36000-1);//时钟预分频数为36000
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//采样分频倍数1,未明该语句作用。
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//上升模式
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//清除更新标志位
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能中断
TIM_Cmd(TIM2,ENABLE);//使能TIM2定时器
TIM_PrescalerConfig(TIM2, 0xEA5F, TIM_PSCReloadMode_Immediate); //可选?@brief Configures the TIMx Prescaler.
}
3.输入捕捉
引脚上一旦出现一个有效边沿(可以配置为上升、下降或者上升下降均触发),那么定时器计数器CNT里面的值就会被相应的Capture/Compare X Register保存下来。这里X可以是1,2,3,4任何一个。并且中断标志位被置位。但是此时TIM的计数寄存器CNT却不管这一事件的发生,继续自己的计数。此功能可以用来测量外部信号的脉宽或者是周期。
TIMx_ICInitStructure的属性有:
- TIM_Channel:通道选择,TIM_Channel_1,TIM_Channel_2,TIM_Channel_3,TIM_Channel_4
- TIM_ICPolarity:捕捉信号的高低电平,TIM_ICPolarity_BothEdge,TIM_ICPolarity_Rising,TIM_ICPolarity_Falling
- TIM_ICSelection:定时器的4个通道并不是完全独立的,而是1、2一组,3、4一组,同组之间的通道是有联系的。也就是可以出现交叉触发。而TIM_ICSelection就是选择要不要使用交叉来触发。
TIM_ICSelection_DirectTI ,TIM Input 1, 2, 3 or 4 is selected to be connected to IC1, IC2, IC3 or IC4, respectively;
TIM_ICSelection_IndirectTI, TIM Input 1, 2, 3 or 4 is selected to be connected to IC2, IC1, IC4 or IC3, respectively;
TIM_ICSelection_TRC ,TIM Input 1, 2, 3 or 4 is selected to be connected to TRC. - TIM_ICPrescaler:预分频
- TIM_ICFilter:滤波器
例如:
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //通道1
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //无预分频器
TIM5_ICInitStructure.TIM_ICFilter = 0x00; //IC1F=0000 //无滤波器
TIM_ICInit(TIM5, &TIM5_ICInitStructure);
4. 输出比较
计数值达到一共预存数后,将会产生一次输出。
可以用于产生PWM波形。
例如:
TIM_OCInitStructure.TIM_OCMode= TIM_OCMode_PWM1; //TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable;//这个暂时不知道,stm32固件库里没有搜到。应该是定时器输出声明使能的意思
TIM_OCInitStructure.TIM_Pulse =CCR1_Val;//设置了待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High; //TIM输出比较极性高
TIM_OC1Init(TIM3,&TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);//使能或者失能TIMx在CCR1上的预装载寄存器
八、EXTI
EXTI管理了控制器的23个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。