持续更新的STM32个人学习笔记
STM32
建立一个新工程
步骤:
建立工程文件夹,Keil中新建工程,选择型号
工程文件夹中建立Start、Library、User文件夹,复制固件库中的文件到 工程文件夹
Start:STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm
Library:STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\inc
STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src
User:STM32F10x_StdPeriph_Lib_V3.5.0\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template
工程里建立Start、Library、User同名称的group,然后将文件夹内的文件添加到工程分组里
工程选项,C/C++,Include Paths内声明所有包含头文件的文件夹
工程选项,C/C++,Define内定义USE_STDPERIPH_DRIVER
工程选项,Debug,下拉列表选择对应调试器,Setting –>Flash Download–>Reset and Run
代码测试
#include "stm32f10x.h"
int main(void)
{
RCC->APB2ENR = 0x00000010;//IO端口C时钟使能置1开启时钟
GPIOC->CRH = 0x00300000;//PC13口模式
GPIOC->ODR = 0x00002000;//ODR13口
while(1)
{
}
}



遇到的问题
问题一:ST_LINK一直闪烁
详细可查看这篇博文https://blog.csdn.net/bean_business/article/details/109129337
解决方法:
问题二:Keil无法在非管理员身份下运行
解决方法
问题三:插入头文件但总是出现红色波浪线(以及编译后报错)
详细可查看这篇博文https://blog.csdn.net/qq_45899177/article/details/135854420
解决方法
问题四 :显示 0 Error(s) 1Warning(s)
解决方法
库函数实现点灯
#include "stm32f10x.h"
int main(void)#include "stm32f10x.h" // Device header
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//时钟配置
GPIO_InitTypeDef GPIO_InitSture;//结构体定义起别名
结构体内部要配置的3个
GPIO_InitSture.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitSture.GPIO_Pin = GPIO_Pin_13;
GPIO_InitSture.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitSture);//结构体地址传递
GPIO_SetBits(GPIOC,GPIO_Pin_13);//高电平设置
// GPIO_ResetBits(GPIOC,GPIO_Pin_13);//低电平设置
while(1)
{
}
}
GPIO通用输入输出口
简介
GPIO是通用输入输出端口的简称
GPIO的基本结构
电路图简介
- 钳位(保护)二极管:保护二极管和上/下拉电阻,防止从外部引脚输入的电压过高或过低。
- P-MOS管和N-MOS管:二者构成CMOS反相器电路,使端口具有推挽输出和开漏输出两种输出模式
- 推挽输出,输入高电平–>反相–>P-MOS管导通–>N-MOS管关闭–>输出高电平(低电平时N-MOS管导通)(低电平0V、高电平3.3V)(P-MOS灌电流,N-MOS拉电流)
- 开漏输出,P-MOS管不工作,输低电平,N-MOS导通,输出接地。(输0接地、输1断开)(无上拉电阻)
- 输出数据寄存器
- 复用功能输出
- 输入数据寄存器
- 复用功能输入
- 模拟输入输出
工作模式简介
模式名称 | 性质 | 特征 |
---|---|---|
浮空输入 | 数字输入 | 可读取引脚电平,引脚悬空 ,则电平不确定 |
上拉输入 | 数字 | 可读取引脚电平,内部连接上拉电阻 ,悬空时默认高电平 |
下拉输入 | 数字 | 可读取引脚电平,内部连接下电阻,悬空时默认低电平 |
模拟输入 | 模拟 | GPIO无效,引脚直接接入内部ADC |
开漏输出 | 数字 | 可输出引脚电平,高电平为高阻态,低电平接VSS |
推挽输出 | 数字 | 可输出引脚电平,高电平接VDD,低电平接VSS |
复用开漏输出 | 数字 | 由片上外设控制,高电平为高阻态,低电平接VSS |
复用推挽输出 | 数字 | 由片上外设控制,高电平接VDD,低电平接VSS |
LED和蜂鸣器
LED
蜂鸣器
PNP三极管最好接上面,NPN三极管最好接下面
GPIO
点亮/熄灭LED灯
#include "stm32f10x.h" // Device header
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//点亮LED灯//
// GPIO_ResetBits(GPIOA,GPIO_Pin_0);
// GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
//熄灭LED灯//
// GPIO_SetBits(GPIOA,GPIO_Pin_0);
// GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
while(1)
{
}
}
LED闪烁
#include "stm32f10x.h" // Device header
#include "Delay.h" // 延时函数
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出,低电平有驱动能力
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
//点亮LED
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
// GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);//延时
//熄灭LED
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
// GPIO_SetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);//延时
}
}
Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
经验证任意点亮LED代码与任意LED熄灭代码混用也可以实现功能(即writebit和Setbit一起用也OK)
以及如果需要更改字符,比如用0,1来表示高低电平,则需要调用GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);并将Bit_SET更改为(BitAction) 1,Bit_RESET更改为(BitAction) 0
问题探讨
一:LED操作中将LED灯的短脚接负极,长脚接A0,LED灯仍然保持闪烁
LED流水灯
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(500);
}
}
问题探讨
二:为什么是GPIOA而不是GPIOB
(哈哈,当你提出这种问题的时候就说明其实还是有发动自己的小脑瓜在思考嘛:happy:)
蜂鸣器
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//连接B12口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
while(1)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}
刚刚提出的问题在这里是不是就用上了呢,哈哈,蜂鸣器我们用的是B12口,所以使用了GPIOB寄存器~🖐
GPIO输入
按键驱动
键盘须知:
键盘分类:
按键驱动:
按键开关的两端分别连接在行线和列线上,通过键盘开关机械触电的断开、闭合,其按键电压输出波形会有一段起伏,按下开关是会有5~10ms左右的抖动期。
消除抖动的方法:
关于STM32C语言相关介绍可查看这篇文档 C.md
按键控制LED
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum;
int main(void)
{
LED_Init();
Key_Init();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
LED1_Turn();
}
if(KeyNum == 2)
{
LED2_Turn();
}
}
}
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 |GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
Delay_ms(20);
KeyNum = 1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
LED.c
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);
}
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
}
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
void LED2_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
}
遇到的问题
问题一:代码无法自动补全
法一:
详情请看这篇博文https://blog.csdn.net/qq_45138815/article/details/117465568
法二:
问题二:只有一个灯闪烁(在第一次调试时两个灯都亮)
问题三:Hardware\Key.c(18): error: #20: identifier “unit8_t” is undefined
光敏传感器控制蜂鸣器
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
int main(void)
{
Buzzer_Init();
LightSensor_Init();
while(1)
{
if(LightSensor_Get() ==1)
{
Buzzer_ON();
}
else
{
Buzzer_OFF();
}
}
}
LightSensor.c
#include "stm32f10x.h" // Device header
void LightSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}
Buzzer.c
#include "stm32f10x.h" // Device header
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
}
OLED
简介
OLED是有机发光二极管
发光原理:
使用0.96寸OLED模块
调试方式
驱动函数
函数 | 作用 |
---|---|
OLED_Init(); | 初始化 |
OLED_Clear(); | 清屏 |
OLED_ShowChar(1,1,‘A’); | 显示一个字符 |
OLED_ShowString(1,3,‘HelloWorld!’); | 显示字符串 |
OLED_ShowNum(2,1,12345,5); | 显示十进制数字 |
OLED_ShowSignedNum(2,7,-66,2); | 显示有符号十进制数字 |
OLED_ShowHexNum(3,1,0xAA55,4); | 显示十六进制数字 |
OLED_ShowBinNum(4,1,0xAA55,16); | 显示二进制数字 |
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
int main(void)
{
OLED_Init();
OLED_ShowChar(1, 1, 'A');
OLED_ShowString(1,3,"HelloWorld!");
OLED_ShowNum(2,1,12345,5);
OLED_ShowSignedNum(2,7,-66,2);
OLED_ShowHexNum(3,1,0xAA55,4);
OLED_ShowBinNum(4,1,0xAA55,16);
while(1)
{
}
}
EXIT外部中断
中断
概念:指内部或外部事件使CPU暂停当前程序,转去执行中断服务程序的一种工作机制。
STM32最多能够接受84个中断,包括16个内核中断和68个外部中断(STM32F103系列只有60个中断)
STM32将中断分为5组。
组别 | AIRCR[10:8] | BIT[7:4]分配情况 | 分配结果 |
---|---|---|---|
0 | 111 | 0:4 | 0位抢占优先级(取值为0),4位响应优先级(取值为0-15) |
1 | 110 | 1:3 | 1位抢占优先级(取值为0-1),3位响应优先级取值为0-17) |
2 | 101 | 2:2 | 2位抢占优先级(取值为0-3),2位响应优先级取值为0-3) |
3 | 100 | 3:1 | 3位抢占优先级(取值为0-7),1位响应优先级(取值为0-1) |
4 | 011 | 4:0 | 4位抢占优先级(取值为0-15),0位响应优先级(取值为0) |
中断优先级:
中断嵌套:
EXIT外部中断
对射式红外传感器计次
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init();
countsensor_Init();
OLED_ShowString(1,1,"Count");
while(1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5);
}
}
CountSensor.c
#include "stm32f10x.h" // Device header
uint16_t CountSensor_Count;
void countsensor_Init(void)
{
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//配置AFIO 将EXTI的第14个线路配置为中断模式,下降沿触发,开启中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14 ;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
//EXTI_Trigger_Rising上升沿触发,即遮挡时计数器加一
//EXTI_Trigger_Falling下降沿触发,即遮挡后挪开挡光片时计数器加一
EXTI_Init(&EXTI_InitStructure);
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
//返回计数器的值
uint16_t CountSensor_Get(void)
{
return CountSensor_Count;
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) == SET)
{
CountSensor_Count++;
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
遇到的问题
问题1:为什么光敏传感器遮挡后还是很亮
解答:
问题2:为什么红外传感器遮挡后仍然亮
解答:
旋转编码器计次
Encoder.c
#include "stm32f10x.h" // Device header
int16_t Encoder_Count;
void Encoder_Init(void)
{
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//配置AFIO 将EXTI的第14个线路配置为中断模式,下降沿触发,开启中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 ;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
//进行计次
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
Encoder_Count = 0;
return Temp;
}
//向PB0口转动一下减一
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)
{
Encoder_Count --;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
//向PB1口转动一次加一
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) == SET)
{
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)
{
Encoder_Count ++;
}
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Num;
int main(void)
{
OLED_Init();
Encoder_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
Num += Encoder_Get();
OLED_ShowSignedNum(1,5,Num,5);
}
}
TIM定时中断
简介TIM
定时器 | TIMx | 配备 | 功能 | 总线 |
---|---|---|---|---|
通用定时器 | TIM2、TIM3、 TIM4、 TIM5 | 1个16位自动加载计数器,1个16位可编程预分频器,4个独立通道 | 使用外部信号控制定时器、产生中断或DMA、触发输入作为外部时钟或按周期的电流管理··· | APB1 |
高级定时器 | TIM1、TIM8 | 1个16位自动加载计数器,1个16位可编程预分频器,4个独立通道 | 适合更复杂的场景 | APB2 |
基本定时器 | TIM6、TIM7 | 各配备1个 16位自动装载计数器、 | 产生DAC触发信号、也可当作通用的16位时基计数器 | APB1 |
(STM32F103C8T6定时器:TIM1、TIM2、TIM3、TIM4)
除内部时钟源以外其他三种时钟源均通过TRGI(触发)输入。
通用定时器的时钟输入源
计数器计数频率:CK_CNT = CK_PSC/(PSC + 1)
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
定时器中断
定时时间的计算:
定时时间由TIM_TimeBaseInitTypeDef中的TIM_Prescaler和TIM_Period进行设定
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num");
while(1)
{
OLED_ShowNum(1,5,Num,5);
//CNT计数值的情况
//OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
//定时器中断函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Num++;
//清除标志位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);//默认调用内部时钟,不写也行
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 10000 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 7200 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInisStructure);
//手动清除更新标志位
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
/*
//定时器中断函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
//清除标志位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
*/
定时器外部中断
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num");
OLED_ShowString(2,1,"CNT:");
while(1)
{
OLED_ShowNum(1,5,Num,5);
//CNT计数值的情况
OLED_ShowNum(2,5,Timer_GetCounter(),5);
}
}
//定时器中断函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Num++;
//清除标志位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//ETR外部时钟2
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0X00);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period =10 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler =1 - 1;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInisStructure);
//手动清除更新标志位
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
//使能更新中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
NVIC_Init(&NVIC_InitStructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
/*
//定时器中断函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
//清除标志位
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
*/
TIM输出比较
输出PWM波形(驱动电机)
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
输出比较的8种模式:
模式 | 描述 |
---|---|
冻结 | CNT = CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT = CCR时,REF置有效电平 |
匹配时置无效电平 | CNT = CCR 时,REF置无效电平 |
匹配时电平翻转 | CNT = CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT>=CCR时,REF置无效电平向下计数:CNT>CCR时,REF置无效电平,CNT<=CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT>=CCR时,REF置有效电平向下计数:CNT>CCR时,REF置有效电平,CNT<=CCR时,REF置无效电平 |
舵机
根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
输入信号脉冲宽度 | 舵机输出轴转角 |
---|---|
0.5ms | -90° |
1ms | -45° |
1.5ms | -0° |
2ms | 45° |
2.5ms | 90° |
直流电机
将电能转换为机械能,电极正接,电机正转。
属于大功率器件,需要配合电机驱动电路来操作。
PWM驱动LED呼吸灯
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while(1)
{
for(i=0;i<=100;i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=0;i<=100;i++)
{
PWM_SetCompare1(100-i);
Delay_ms(10);
}
}
}
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//AFIO 重映射更改复用的引脚
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//打开AFIO时钟
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//重映射引脚
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//接触调试端口
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用开漏推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);//默认调用内部时钟,不写也行
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 1000 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 720 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInisStructure);
//初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitstructure;
TIM_OCStructInit(&TIM_OCInitstructure);
TIM_OCInitstructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较模式
TIM_OCInitstructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性
TIM_OCInitstructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitstructure.TIM_Pulse = 0;//设置CCR
TIM_OC1Init(TIM2,&TIM_OCInitstructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
//单独更改通道1的CCR值
TIM_SetCompare1(TIM2,Compare);
}
PWM驱动舵机
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;
float Angle;
int main(void)
{
OLED_Init();
Servo_Init();
Key_Init();
OLED_ShowString(1,1,"Angle:");
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum==1)
{
Angle += 30;
if(Angle > 180)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1,7,Angle,3);
}
}
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用开漏推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);//默认调用内部时钟,不写也行
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 20000 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 72 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInisStructure);
//初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitstructure;
TIM_OCStructInit(&TIM_OCInitstructure);
TIM_OCInitstructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较模式
TIM_OCInitstructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性
TIM_OCInitstructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitstructure.TIM_Pulse = 0;//设置CCR
TIM_OC2Init(TIM2,&TIM_OCInitstructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
//单独更改通道2的CCR值
TIM_SetCompare2(TIM2,Compare);
}
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
//舵机初始化
void Servo_Init(void)
{
PWM_Init();
}
//舵机设置角度
void Servo_SetAngle(float Angle)
{
//对Angle 进行缩放
//0 -->500 180 -->2500
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
PWM驱动直流电机
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Key_Init();
OLED_ShowString(1,1,"Speed:");
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
Speed += 20;
if(Speed > 100)
{
Speed = -100;
}
}Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1,7,Speed,3);
}
}
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用开漏推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM2);//默认调用内部时钟,不写也行
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 1000 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 720 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInisStructure);
//初始化输出比较单元
TIM_OCInitTypeDef TIM_OCInitstructure;
TIM_OCStructInit(&TIM_OCInitstructure);
TIM_OCInitstructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较模式
TIM_OCInitstructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较的极性
TIM_OCInitstructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
TIM_OCInitstructure.TIM_Pulse = 0;//设置CCR
TIM_OC3Init(TIM2,&TIM_OCInitstructure);
//启动定时器
TIM_Cmd(TIM2,ENABLE);
}
void PWM_SetCompare3(uint16_t Compare)
{
//单独更改通道3的CCR值
TIM_SetCompare3(TIM2,Compare);
}
Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
//初始化
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
PWM_Init();
}
//设置速度函数‘
void Motor_SetSpeed(int8_t Speed)
{
//正转
if(Speed >= 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
//反转
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}
遇到的问题
电机转的慢,带不动风扇
解决方法:
TIM输入捕获
本节课使用测周法
输入捕获模式测频率
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
PWM_SetPrescaler(720-1); //Freq = 72M / (PSC + 1) /100
PWM_SetCompare1(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,IC_GetFreq(),5);
}
}
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
// GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}
IC.c
#include "stm32f10x.h" // Device header
void IC_Init(void)
{
/*RCC开启时钟,打开GPIO,TIM时钟
GPIO初始化,把GPIO配置为输入模式(上拉/浮空)
配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
配置输入捕获单元
选择从模式的触发源 -->TIFP1
选择触发之后执行的操作 执行Reset操作
调用TIM_Cmd函数,开启定时器
*/
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM3);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 65536 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 72 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInisStructure);
//初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//捕获通道 --> 使能TIMx通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入滤波器,0x0 ~ 0xF(不使用:0),滤波器计次不会改变信号的原有频率
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//输入捕获的触发边沿,
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//确定输入捕获预分频器,对信号本身进行计次,会改变频率
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择捕获通道ICx的信号输入通道
TIM_ICInit(TIM3,&TIM_ICInitStructure);
//配置TRGI的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
//调用TIM_Cmd函数,启动定时器
TIM_Cmd(TIM3,ENABLE);
}
//返回最新一个周期的频率值
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}
问题
为什么显示为0000Hz
解决方法:
PWMI模式测频率占空比
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
OLED_ShowString(2,1,"Duty:00%");
PWM_SetPrescaler(720-1); //Freq = 72M / (PSC + 1) /100
PWM_SetCompare1(50); //Duty = CCR / 100
while(1)
{
OLED_ShowNum(1,6,IC_GetFreq(),5);
OLED_ShowNum(2,6,IC_GetDuty(),2);
}
}
IC.c
#include "stm32f10x.h" // Device header
void IC_Init(void)
{
/*RCC开启时钟,打开GPIO,TIM时钟
GPIO初始化,把GPIO配置为输入模式(上拉/浮空)
配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
配置输入捕获单元
选择从模式的触发源 -->TIFP1
选择触发之后执行的操作 执行Reset操作
调用TIM_Cmd函数,开启定时器
*/
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//选择时基单元的时钟
TIM_InternalClockConfig(TIM3);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 65536 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 72 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInisStructure);
//初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//捕获通道 --> 使能TIMx通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入滤波器,0x0 ~ 0xF(不使用:0),滤波器计次不会改变信号的原有频率
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//输入捕获的触发边沿,
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//确定输入捕获预分频器,对信号本身进行计次,会改变频率
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择捕获通道ICx的信号输入通道
TIM_ICInit(TIM3,&TIM_ICInitStructure);
TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);//自动配置为相反模式
//第一种方法
// TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//捕获通道 --> 使能TIMx通道1
// TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入滤波器,0x0 ~ 0xF(不使用:0),滤波器计次不会改变信号的原有频率
// TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//输入捕获的触发边沿,
// TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//确定输入捕获预分频器,对信号本身进行计次,会改变频率
// TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//选择捕获通道ICx的信号输入通道
// TIM_ICInit(TIM3,&TIM_ICInitStructure);
//配置TRGI的触发源
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//配置从模式为Reset
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
//调用TIM_Cmd函数,启动定时器
TIM_Cmd(TIM3,ENABLE);
}
//返回最新一个周期的频率值
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}
//获取占空比
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) +1) * 100 /(TIM_GetCapture1(TIM3) + 1);
}
遇到的问题
为什么占空比显示为00%
解决方法:
TIM编程器接口
编码器接口简介
正转都向上计数,反转都向下计数
编码器测速
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"
int16_t Speed;
int main(void)
{
OLED_Init();
Timer_Init();
Encoder_Init();
OLED_ShowString(1,1,"Speed:");
while(1)
{
OLED_ShowSignedNum(1,7,Speed,5);
}
}
//定时器中断函数
void TIM2_IRQHandler(void)
{
//检查中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
Speed = Encoder_Get();
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Encoder.c
#include "stm32f10x.h" // Device header
void Encoder_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
//GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInisStructure;
TIM_TimeBaseInisStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInisStructure.TIM_CounterMode = TIM_CounterMode_Up ;
TIM_TimeBaseInisStructure.TIM_Period = 65536 - 1;//自动重装 ARR 1~65536
TIM_TimeBaseInisStructure.TIM_Prescaler = 1 - 1 ;//预分频 PSC 1~65536
TIM_TimeBaseInisStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInisStructure);
//配置两个通道的滤波器和极性
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//捕获通道 --> 使能TIMx通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入滤波器,0x0 ~ 0xF(不使用:0),滤波器计次不会改变信号的原有频率
TIM_ICInit(TIM3,&TIM_ICInitStructure);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//捕获通道 --> 使能TIMx通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //选择输入滤波器,0x0 ~ 0xF(不使用:0),滤波器计次不会改变信号的原有频率
TIM_ICInit(TIM3,&TIM_ICInitStructure);
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);
TIM_Cmd(TIM3,ENABLE);
}
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3,0);
return Temp;
}
作者:MLIM_air