stm32单片机学习入门_1
此系列博客就是记录我跟着江科大的视频的学习
第一章_入门

这里介绍一下DMA , 使内存直接访问, 这个可以帮助CPU完成搬运大量数据这样的繁杂任务


第二章新建工程

对于标准库来说建工程是比较麻烦的
Start 里面放的是启动文件
Library 里面放的是标准库文件
User 里面放的是用户的文件![]()

conf.h里面
这一项比较特殊容易忘掉, 这一项是用来使我们包的头文件里面会包含conf.h这个文件, 这样我们只要包了![]()
这个头文件, 其他所有的文件我们相当于都包含了
同时这个32单片机与51不同之处有很多, 并且这个标准库没有图形化配置界面, 都是自己用结构体去配置相应的模式
第三章GPIO
GPIO的低十六位有着引脚相连, 高十六位没有
这个触发器其实名字叫做施密特触发器, 用于整形波形
推挽输出高电平时有点亮灯的驱动能力, 而开漏输高电平出没有驱动能力
1GPIO 输出
1点亮一个小灯
int main()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
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);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
Delay_ms(500);
}
}
2 GPIO 输入

第四章OLED调试

第五章中断
1对射式红外传感器计次
像PA0 和 PB0 不能同时触发中断
事件响应不会触发中断, 而是对其他外设产生影响
这个EXTI结构图很重要


按键检测不建议用外部中断

2代码部分
第一步, 配置RCC:GPIO和AFIO需要配置时钟
第二步,配置GPIO
第三步, 配置AFIO
第四步, 配置EXTI
第五步, 配置NVIC
代码展示
#include "stm32f10x.h" // Device header
uint16_t CountSensor_Count;
void CountSensor_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//配置GPIO
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外部引脚选择
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
//EXTI初始化
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
//NVIC的初始化
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);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组要求一致
}
uint16_t CountSensor_Get()
{
return CountSensor_Count;
}
//中断函数名字在启动文件里面找
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14)==SET)
{
if(GPIO_ReadInputDataBit(GPIOB ,GPIO_Pin_14) == 0)
{
CountSensor_Count++;
}
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
在中断里面要进行一些简单的操作, 不要去操作硬件 , 或者执行时间较长的代码
第六章定时器
1定时器中断


自动重装值就是我们设定的值, 计数器达到自动重装寄存器里面的值时, 产生中断 
通用定时器可以用外部时钟
高级定时器的时钟一些特殊功能就是给三项无刷电机用的




代码展示:
Timer.c内容
//extern uint16_t Num;
void Timer_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//配置时钟源
TIM_InternalClockConfig(TIM2);
//配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000-1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//使能中断
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//结构体配置
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)
// {
// Num++;
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
代码解释 :
以上是内部时钟若需要外部时钟作这样修改
同时注意 :
这就一上电就会进入中断, Num显示为1而不是0
解决办法很简单, 就是在开启中断前面, 在BaseInit后面, 加上一个手动调用清除标志位
2PWM驱动呼吸灯












这是将PA0重映射到PA15上, 同时PA15又是调试端口, 那么就要把PA15的复用模式解开
代码展示:
PWM.c
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseINitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋值一个初始值
//GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//解除PA15调试端口的复用
//让他恢复普通GPIO的功能(当你的要重映射的端口是调试端口的时候)
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//引脚重映射
//配置时钟源
TIM_InternalClockConfig(TIM2);
//时基单元初始化
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_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);
//初始化GPIO
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_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
代码解释

3PWM控制舵机
代码展示 :
Servo.c
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
void Servo_SetAngel(float Angel)
{
//y = kx + b 0, 500 180, 2500带入就可以的到放缩比例
PWM_SetCompare2(Angel/180*2000 +500);
}
这里用角度转换了一下
PWM.c
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseINitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);//给结构体赋值一个初始值
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置时钟源
TIM_InternalClockConfig(TIM2);
//时基单元初始化
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;//PSC
TIM_TimeBaseINitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseINitStructure);
//初始化输出比较单元
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);
//初始化GPIO
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_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
关键是
这里PWM的周期要为20ms那么频率就是1/0.02 = 100/2hz, 72m/72 为1mHz 那么period为20000-1就可以, 那么高电平1.5ms就是中间位置对应比较值就是1500 -90°为500 90°为2500
4PWM控制直流电机
这里发现一个问题, 用高级定时器输出PWM的时候要打开这个开关, 要不然不能输出PWM

代码展示:
Motor.c
PA4 和PA5 就是来控制电机正反转
#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);
}
}
这样Speed就是1~100, 对应着相应的占空比
5输入捕获测量频率和占空比
代码展示:
IC.c
void IC_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructrue);
//配置时钟源
TIM_InternalClockConfig(TIM3);
//配置时机单元
TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseInitStructure.TIM_Period = 65536-1;
TIM_BaseInitStructure.TIM_Prescaler = 72-1;
TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_BaseInitStructure);
//初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;//不会改变信号频率
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
//触发源选择
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
//配置重模式
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
//启动定时器
TIM_Cmd(TIM3, ENABLE);
}
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3)+1);
}
代码解释:
测量占空比就是多一步处理
6编码器测速


最下面的模式最常用, 精度最高

上拉和下拉的选择原则就是看外部模块, 外部模块默认是低电平就设置下拉, 同理相反, 防止电频打架, 不确定和外部信号输出功率非常小, 那么就选择浮空输入
代码展示:
Encoder.c
void Encoder_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
//配置GPIO
GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructrue);
//配置时钟源 , 编码器接口会接管时钟(就是外部时钟)
//TIM_InternalClockConfig(TIM3);
//配置时机单元
TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseInitStructure.TIM_Period = 65536-1;
TIM_BaseInitStructure.TIM_Prescaler = 1-1;
TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_BaseInitStructure);
//初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;//不会改变信号频率
//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//这个也可以看为极性设置
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;//不会改变信号频率
//TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//这个也可以看为极性设置
TIM_ICInit(TIM3, &TIM_ICInitStructure);
//配置编码器接口模式
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12,TIM_ICPolarity_Falling,TIM_ICPolarity_Falling);
TIM_Cmd(TIM3, ENABLE);
}
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0);
return Temp;
}
代码解释
最后用临时变量返回, 并且每隔一段实践调用就可以显示速度
更多推荐







所有评论(0)