此系列博客就是记录我跟着江科大的视频的学习

第一章_入门

这里介绍一下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;
}

代码解释最后用临时变量返回, 并且每隔一段实践调用就可以显示速度

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐