首页
关于本站
其他站点
意见反馈
更多
我的目标
假期规划
Search
1
分享一下我觉得还不错的摘抄句子
251 阅读
2
过年啦
237 阅读
3
看职高三年,又是三年,好快!
153 阅读
4
聊那天给学校做个全景地图,现在想起也挺尴尬的
147 阅读
5
这两个有趣的md5
116 阅读
日常生活
随手一记
学习踩坑
技术分享
学习笔记
STM32
登录
Search
你的糖来啦
累计撰写
35
篇文章
累计收到
6
条评论
首页
栏目
日常生活
随手一记
学习踩坑
技术分享
学习笔记
STM32
页面
关于本站
其他站点
意见反馈
我的目标
假期规划
搜索到
35
篇与
的结果
2024-06-23
STM32使用PWM驱动舵机
舵机的工作原理以9g舵机为例有三根线:电源正极 电源负极 信号线(输入PWM)我么可以通过PWM来控制舵机转动,其中改变PWM的占空比可以控制舵机角度,如下图所示:使用PWM控制舵机的要求以9g舵机为例,要求控制PWM为50Hz 周期为20ms代码部分PWM.c#include "stm32f10x.h" // Device header void PWM_Init(void) { //启动TIM GPIO RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; //需要使用GPIO来输出PWM PWM来自片上外设TIM 所以设置为复用推挽输出 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 //CK_PSC时钟频率 PSC分频 ARR自动重装 //PWM频率 = CK_PSC/(PSC+1)/(ARR+1) //PWM占空比 = CCR/(ARR+1) //PWM分辨率 = 1/(ARR+1) 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 = 20000 - 1; //ARR TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //CK_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_OC2Init(TIM2, &TIM_OCInitStructure); TIM_Cmd(TIM2,ENABLE); } //更改CCR占空比用的 void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare2(TIM2,Compare); } 控制舵机角度 servo.c#include "stm32f10x.h" // Device header #include "PWM.h" void Servo_Init() { PWM_Init(); } void Servo_SetAngle(float Angle) { PWM_SetCompare2(Angle / 180 *2000 + 500); } 按钮部分 Key.c#include "stm32f10x.h" // Device header #include "Delay.h" /** * 函 数:按键初始化 * 参 数:无 * 返 回 值:无 */ void Key_Init(void) { /*开启时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟 /*GPIO初始化*/ 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); //将PB1和PB11引脚初始化为上拉输入 } /** * 函 数:按键获取键码 * 参 数:无 * 返 回 值:按下按键的键码值,范围:0~2,返回0代表没有按键按下 * 注意事项:此函数是阻塞式操作,当按键按住不放时,函数会卡住,直到按键松手 */ uint8_t Key_GetNum(void) { uint8_t KeyNum = 0; //定义变量,默认键码值为0 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //读PB1输入寄存器的状态,如果为0,则代表按键1按下 { Delay_ms(20); //延时消抖 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按键松手 Delay_ms(20); //延时消抖 KeyNum = 1; //置键码为1 } if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //读PB11输入寄存器的状态,如果为0,则代表按键2按下 { Delay_ms(20); //延时消抖 while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按键松手 Delay_ms(20); //延时消抖 KeyNum = 2; //置键码为2 } return KeyNum; //返回键码值,如果没有按键按下,所有if都不成立,则键码为默认值0 } main.c#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Key.h" #include "servo.h" uint8_t Key_Num; float Angle; int main(void) { /*模块初始化*/ OLED_Init(); //OLED初始化 Key_Init(); Servo_Init(); OLED_ShowString(1,1,"Angle:"); while (1){ Key_Num = Key_GetNum(); if(Key_Num == 1) { Angle += 30; if(Angle > 180) { Angle = 0; } } Servo_SetAngle(Angle); OLED_ShowNum(1, 7, Angle, 3); } }
2024年06月23日
27 阅读
0 评论
0 点赞
2024-06-23
STM32使用PWM驱动LED呼吸灯
使用PWM来实现LED呼吸灯的效果步骤开启并配置TIM和GPIO时钟GPIO需要设置成为复用推挽输出根据实际需要配置合适的时基单元(PWM频率)配置好输入比较寄存器具体如下注意:本代码是使用比较通道1,对用在PA0上输出PWM主要代码 PWM.c#include "stm32f10x.h" // Device header void PWM_Init(void) { //启动TIM GPIO RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; //需要使用GPIO来输出PWM PWM来自片上外设TIM 所以设置为复用推挽输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); //配置TIM //CK_PSC时钟频率 PSC分频 ARR自动重装 //PWM频率 = CK_PSC/(PSC+1)/(ARR+1) //PWM占空比 = CCR/(ARR+1) //PWM分辨率 = 1/(ARR+1) 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; //CK_PSC TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //ARR 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); } //更改CCR占空比用的 void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM2,Compare); } main.c#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "PWM.h" int main(void) { /*模块初始化*/ OLED_Init(); //OLED初始化 PWM_Init(); OLED_ShowString(1,1,"Pulse:"); while (1) { for(uint16_t i = 0;i <= 100; i++) { PWM_SetCompare1(i); OLED_ShowNum(2,1,i,3); //屏幕上显示占空比 可删除 Delay_ms(20); } for(uint16_t i = 0;i <= 100; i++) { PWM_SetCompare1(100 - i); OLED_ShowNum(2,1,100 - i,3); Delay_ms(20); } } }
2024年06月23日
15 阅读
0 评论
0 点赞
2024-06-21
STM32定时器定时中断
图片来源bilibil 江协科技 上图为定时器中断结构,我们使用定时器中断的操作步骤:选择时钟源(如上图左内部时钟、外部时钟、其他定时器、通道捕获)初始化时基单元配置中断输出控制配置NVIC使能时钟运行控制以下为一个示例 1s触发一次中断#include "stm32f10x.h" // Device header void Timer_Init(void) { //启动APB1下的TIM2通用定时器 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //选择TIM2通用定时器 系统默认配置使用 可以不配置 但是规范流程 写一下 TIM_InternalClockConfig(TIM2); //初始化时基单元 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSturcture; //选择时钟分频 这里是1分频就是不分频 TIM_TimeBaseInitSturcture.TIM_ClockDivision = TIM_CKD_DIV1; //选择计数模式 向上计数 TIM_TimeBaseInitSturcture.TIM_CounterMode = TIM_CounterMode_Up; //因为从0开始所以需要减一 //stm32103c8单片机频率是72Mhz; 72M分频(除以)7200 = 10000 的意思是单片机进行7200分频得到每秒10000个震动 ,然后计数设置成10000每10000次触发一次更新 TIM_TimeBaseInitSturcture.TIM_Period = 10000 -1; TIM_TimeBaseInitSturcture.TIM_Prescaler = 7200 -1; //高级定时器才有 这里填0 TIM_TimeBaseInitSturcture.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitSturcture); //手动清除一次标志位 系统初始化时手动触发了更新事件 因此手动清除一次标志位 可以理解不清除的话代码单片机上电就会从1开始 但是计数是从0开始 TIM_ClearFlag(TIM2,TIM_FLAG_Update); //中断输出控制 允许TIM2定时器更新 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); //使能TIM2 TIM_Cmd(TIM2,ENABLE); } //配置TIM2的中断函数 这个格式固定可以开始文件中找到 void TIM2_IRQHandler(void) { //检查中断标志位 规范步骤 if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) { //需要中断执行的代码块 //代码结束清除标志位 不然退出不了中断函数 TIM_ClearITPendingBit(TIM2,TIM_IT_Update); } }
2024年06月21日
10 阅读
0 评论
0 点赞
2024-06-19
STM32旋转编码器计次
使用外部中断方式在oled屏幕上显示计次步骤:-打开GPIO、AFIO外设并配置 -配置EXTI、NVIC(EXTI、NVIC这些模块已经自动准备就绪,无需额外的启动步骤)-配置中断函数不断获取被中断函数操作的变量,把变量信息显示在oled屏幕上#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Encoder.h" int16_t Count; int main(void) { /*模块初始化*/ OLED_Init(); //OLED初始化 Encoder_Init(); //旋转编码器初始化 /*OLED显示*/ OLED_ShowString(1, 1, "Count:"); //1行3列显示字符串HelloWorld! while (1) { Count += Count_Get(); OLED_ShowSignedNum(2,1,Count,5); } } 配置外部中断 Encoder.c#include "stm32f10x.h" // Device header int16_t Count_num; void Encoder_Init(void) { //开启GPIO、AFIO的外设 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2ENR_AFIOEN,ENABLE); GPIO_InitTypeDef GPIO_InitStrcuture; //上拉输入方式 GPIO_InitStrcuture.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStrcuture.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStrcuture); //配置AFIO 不同GPIO组也不能使用相同的GPIO_PinSourceX GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1); //配置EXTI EXTI_InitTypeDef EXTI_InitStructuer; EXTI_InitStructuer.EXTI_Line = EXTI_Line0 | EXTI_Line1; EXTI_InitStructuer.EXTI_LineCmd = ENABLE; //这里设置的是外部中断 EXTI_InitStructuer.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructuer.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_Init(&EXTI_InitStructuer); //配置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 Count_Get(void) { int16_t temp = Count_num; Count_num = 0; return temp; } void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) == SET) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0) { while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0); if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向 { Count_num --; //此方向定义为反转,计数变量自减 } } EXTI_ClearITPendingBit(EXTI_Line0); } } void EXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1) == SET) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0) { while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0); if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB0的下降沿触发中断,此时检测另一相PB1的电平,目的是判断旋转方向 { Count_num ++; //此方向定义为反转,计数变量自减 } } EXTI_ClearITPendingBit(EXTI_Line1); } }
2024年06月19日
19 阅读
0 评论
0 点赞
2024-06-19
STM32按钮控制LED
{callout color="#f0ad4e"}前言{/callout} 本文使用的是非是轮询方式。 使用按钮来来控制LED的点亮和熄灭效果,按钮对应的GPIO需要设置为输入模式,本文配置GPIOB的PB1和PB11用作读取按钮使用上拉输入。配置GPIOA的PA1和PA2为位LED的输出脚,LED使用低电平驱动方式首先我们在主函数中不断获取按钮状态来判断是否更新LED的状态#include "stm32f10x.h" // Device header #include "Delay.h" #include "LED.h" #include "Key.h" uint8_t key_num; int main(void) { LED_Init(); Key_Init(); while(1) { //获取按钮返回值 key_num = Key_GetNum(); if(key_num == 1) { LED1_Turn(); } if(key_num == 2) { LED2_Turn(); } } } LED的驱动文件#include "stm32f10x.h" // Device header //初始化LED使用的GPIO void LED_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef InitStructure; InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; InitStructure.GPIO_Speed= GPIO_Speed_50MHz; GPIO_Init(GPIOA, &InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2); } //设置LED的状态 //开 void ON_LED_1(void) { GPIO_ResetBits(GPIOA,GPIO_Pin_1); } void ON_LED_2(void) { GPIO_ResetBits(GPIOA,GPIO_Pin_2); } //关 void OFF_LED_1(void) { GPIO_SetBits(GPIOA,GPIO_Pin_1); } void OFF_LED_2(void) { GPIO_SetBits(GPIOA,GPIO_Pin_2); } //当前状态取反 如果为打开就关闭 如果关闭就打开 void LED1_Turn(void) { //获取当前GPIO状态 并判断 if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0) { GPIO_SetBits(GPIOA,GPIO_Pin_1); } else { GPIO_ResetBits(GPIOA,GPIO_Pin_1); } } void LED2_Turn(void) { //获取当前GPIO状态 并判断 if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0) { GPIO_SetBits(GPIOA,GPIO_Pin_2); } else { GPIO_ResetBits(GPIOA,GPIO_Pin_2); } } 按钮的驱动文件#include "stm32f10x.h" // Device header #include "Delay.h" void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructuer; //输入是设置GPIO为上拉输入 GPIO_InitStructuer.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructuer.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11; //输入时这个参数无效 //GPIO_InitStructuer.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructuer); } uint8_t Key_GetNum(void) { uint8_t keyNum = 0; //按钮1 if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0) { //消除按钮按下的抖动 Delay_ms(20); while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0); //消除按钮松开的抖动 Delay_ms(20); keyNum = 1; } //按钮2 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; }
2024年06月19日
12 阅读
0 评论
0 点赞
1
2
3
...
7