STM32(更新中)

目录

1  时钟(心跳)

1.1  CubeMX基本配置

1.2  外设在时钟上的分配原理

 1.3  时钟树

2  寄存器(地址)

3  GPIO

3.1  GPIO实物

 3.2  GPIO两种结构(推挽/开漏)

3.3  LED

3.4  CUBEMX

3.5  常用函数

3.6  实验1:控制LED交替闪烁

3.7  实验2:按键控制LED的亮灭(1)

3.8  实验2:按键控制LED的亮灭(2)

4  中断

4.1  中断优先级

4.2  实验3:利用外部中断控制LED亮灭(1)(借助其他引脚)

4.3  实验3:利用外部中断控制LED亮灭(2)(按键引脚)

5  UART/USART

5.1  通讯分类

 5.2  USART

5.3  常用函数

5.4  实验4:利用蓝牙模块实现串口通信

5.5  printf/scanf的重定向

6  DMA

6.1  特性及原理

 6.2  FIFO

6.3  传输、工作模式 

6.3.1   传输模式:单次传输与突发传输

6.3.2  工作模式:FIFO模式与直接模式

6.4  实验5:利用空闲中断+串口DMA实现不定长收发

7  HAL库外设驱动原理

7.1  句柄结构体(xx_HandleTypeDef)

7.2  初始化结构体(xx_InitTypeDef)

8  定时器

8.1  基础知识

8.2  PWM

8.3  相关函数

 8.3.1  定时器基本函数

8.3.2  PWM

8.4  实验6:定时器输入捕获实验

8.5  实验7:舵机驱动

9  I2C

9.1  简介

10  SPI

10.1  简介


1  时钟(心跳)

1.1  CubeMX基本配置

不同的时钟源,通过分频或者倍频处理后送到相应的外设单元。
实际应用中根据需要配置外设的时钟控制开关,选择需要的时钟频率,并可关闭不用外设时钟。

这里以STM32F407ZG为例

1.首先根据自己的芯片类型来选择MCU(Microcontroller Unit,微控制器单元)

在这里插入图片描述

 2.SYS 选项卡勾选Serial Wire(不勾选可能会使得无法使用stlink或jlink无法下载)

在这里插入图片描述

 3.在RCC选项卡中将HSE设置为晶振

在这里插入图片描述

4.清板子上晶振原件显示的频率是多少

在这里插入图片描述

1.2  外设在时钟上的分配原理

外设以设定的组别挂在0x0000 0000到0xFFFF FFFF的内存区域中

在这里插入图片描述

以APB1为例,其所附带的外设如下

在这里插入图片描述

 1.3  时钟树

1、HSI:高速内部时钟信号 stm32单片机内带的时钟 (8M频率)精度较差

2、HSE:高速外部时钟信号精度高来源(1)HSE外部晶体/陶瓷谐振器(晶振) (2)HSE用户外部时钟

3、LSE:低速外部晶体 32.768kHz 主要提供一个精确的时钟源 一般作为RTC时钟使用

五大时钟源
LSE:低速外部晶体 32.768kHz 主要提供一个精确的时钟源 一般作为RTC时钟使用

在STM32中,有五个时钟源,为HSI、HSE、LSI、LSE、PLL:

①、HSI是高速内部时钟,RC振荡器,频率为8MHz。

②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。

③、LSI是低速内部时钟,RC振荡器,频率为40kHz。

④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。

⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。
 

2  寄存器(地址

//寄存器编程 
int main()
{
	volatile unsigned int* pointer=(unsigned int*)0x40028000;//volatile的作用是防止变量被编译器优化 
	*pointer+=1;
	*pointer+=2;	//优化后*pointer+=3 对于寄存器来说先后加1和2和直接加3是不一样的 
}
#define pointer (volatile unsigned int*)0x40028000 //预处理不占用内存 

int main()
{
	*pointer+=1;
	*pointer+=2;	//优化后*pointer+=3 对于寄存器来说先后加1和2和直接加3是不一样的 
}

3  GPIO

GPIO(general porpose intput output):通用输入输出端口的简称。可以通过软件控制其输出和输入。stm32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通信,控制以及数据采集的功能。

3.1  GPIO实物

 3.2  GPIO两种结构(推挽/开漏)

 

推挽输出:当IN是高电平时,上边的三极管处于导通状态,下边的三极管处于断开状态,OUT与VCC相连;当IN是低电平时,上边的三极管处于断开状态,下边的三极管处于导通状态,OUT与GND相连

开漏输出:OUT默认与VCC相连,当IN是高电平时,三极管处于断开状态,OUT与VCC相连;当IN是低电平时,三极管处于导通状态,OUT与GND相连

由此可见:推挽输出的控制能力更强,开漏输出有线与的特点(

两个或两个以上与非门的输出端连接在同一条导线上,将这些与非门上的数据(状态电平)用同一条导线输送出去。

3.3  LED

//基地址+偏移地址 
#define GPIOB_CLK (*(volatile unsigned int*)(0x40021000+0x18))
#define GPIOB_CRL (*(volatile unsigned int*)(0x40010c00+0x00))
#define GPIOB_ODR (*(volatile unsigned int*)(0x40010c00+0x0c))

int main()
{
	//1.使能GPIOB的外设时钟
	GPIOB_CLK|=(1<<3); 
	//2.GPIOB配置推挽输出模式
	GPIOB_CRL&= ~(0xf<<(4*0));//清除低4位寄存器 
	GPIOB_CRL|=(2<<0); 
	
	GPIOB_ODR&= ~(0x1<<(1*0));//清除低1位寄存器
	GPIOB_ODR|=(1<<0);
	 
} 

清除位的步骤解释:0xf是1111 

 按位取反:

3.4  CUBEMX

configuration:配置

GPIO output level: 输出电平

GPIO mode:输出模式(推挽输出/开漏输出)

GPIO Pull-up/Pull-down:上拉/下拉

Maximum output speed:最大输出速度

User Label:用户定义标签

这里说一下GPIO上拉下拉:

GPIO通常有三种状态:高电平、低电平和高阻态。高阻态换句话说就是断开状态或浮空态。因此上拉和下拉其中一个强大的理由就是为了防止输入端悬空,使其有确定的状态。减弱外部电流对芯片的产生的干扰。

上拉就是将不确定的信号通过一个电阻提升为高电平,这个上拉的电阻的选择通常有讲究,通常是驱动能力和功耗的平衡,若GPIO为输出为高电平,一般来说,上拉电阻越小,驱动能力越强,但功耗也就越大,同时还要考虑下级电路对驱动能力的要求,上拉电阻选择的合适才能向下级电路提供足够的电流。另外就是数字电路对高低电平都有一个门槛,以上拉电阻为例,输出高电平自然是被拉高,但输出低电平的时候,通常内部的开关管会被导通到地,这必须确保内部导通到地这一段之间的电阻和上拉电阻的比值足够让其电平处在零电平门槛之下。对频率比较高的时候,上拉电阻和开关管漏源级之间的电容和下级电路之间的输入电容会形成“RC延迟”,电阻越大,延迟越大。下拉电阻的选择原理和上拉电阻是一样的。

3.5  常用函数

HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);//初始化引脚
HAL_GPIO_DeInit(GPIOA,GPIO_PIN_1);//初始化PA1引脚为复位状态
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);//将PA1置高电平
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);//将PA1置低电平
HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1);//读取PA1电平状态
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);//取反PA1电平
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);//调用中断回调函数
HAL_GPIO_EXTI_Callback(GPIO_PIN_1);//中断函数具体要响应的动作
HAL_GPIO_LockPin(GPIOA,GPIO_PIN_1);//锁定PA1电平状态

3.6  实验1:控制LED交替闪烁

第一步:基础配置(RCC+时钟树)

第二步:参照数据手册,找到LED对应的引脚,并开启GPIO输出功能:

第三步:生成代码

第四步:写程序

while (1)
  {
    /* USER CODE END WHILE */
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);//高电平时灭
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);//低电平时亮
		HAL_Delay(1000);//延时函数
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);//低电平时亮
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);//高电平时灭
		HAL_Delay(1000);//延时函数
    /* USER CODE BEGIN 3 */
  }

第五步:编译+写入 

3.7  实验2:按键控制LED的亮灭(1)

第一步:基础配置(RCC+时钟树)

第二步:参照数据手册,找到LED和按键(KEY0,KEY1)对应的引脚,并开启GPIO输出/输入功能:

 

第三步:设置上拉输入

上拉输入相当于接一个VCC端,如果按键没有按下,那么IN端与VCC端相连,是高电平

如果按键按下,那么IN端与GND端相连,是低电平

第四步:生成代码

第五步:写程序

while (1)
  {
    /* USER CODE END WHILE */
		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==GPIO_PIN_RESET)
		{
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
		}
		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
		{
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
		}
    /* USER CODE BEGIN 3 */
  }

第六步:编译+写入 

3.8  实验2:按键控制LED的亮灭(2)

第一步:基础配置(RCC+时钟树)

第二步:参照数据手册,找到LED和按键(KEY0,KEY1,WK_UP)对应的引脚,并开启GPIO输出/输入功能:

 

第三步:设置上拉/下拉输入

同理,由于WK_UP按键接VCC,所以配置下拉输入

第四步:生成代码

第五步:写程序

while (1)
  {
    /* USER CODE END WHILE */

		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==GPIO_PIN_RESET)
		{
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
		}
		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
		{
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
		}
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET)
		{
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
		}
    /* USER CODE BEGIN 3 */
 
  }

第六步:编译+写入 

4  中断

中断是指CPU在执行当前程序时系统出现了某种状况,使得CPU必须停止当前程序,而去执行另一段程序来处理的出现的紧急事务,处理结束后CPU再返回到原先暂停的程序继续执行,这个过程就称为中断。

4.1  中断优先级

1)中断优先级分为两种,可编程和不可编程,可编程的表示可以自己修改中断优先级,不可编程的就不能修改

(2)对于STM32中断优先级,决定着内核优先响应谁的中断请求

(3)小值优先原则,中断优先级数值越小,中断就会被优先相应

(4)中断优先级按照优先级分组配置

STM32上只使用M3内核支持的8bit优先级中的高4位bit,也就是STM32支持2^4个优先级

bit7bit6bit5bit4bit3bit2bit1bit0
用于表达优先级未使用,读回为0


使用这4个bit,组织成5组优先级分组,每组分为1个抢占组,1个子优先级组

优先级分组    抢占优先级    子优先级    描述
NVIC_PriorityGroup_0    0    0-15    主-0bit,子-4bit
NVIC_PriorityGroup_1    0-1    0-7    主-1bit,子-3bit
NVIC_PriorityGroup_2    0-3    0-3    主-2bit,子-2bit
NVIC_PriorityGroup_3    0-7    0-1    主-3bit,子-1bit
NVIC_PriorityGroup_4    0-15    0    主-4bit,子-0bit


对于组0,抢占优先级为0,表示他没有抢占优先级,4个bit全部用来表示子优先级。对于组1,抢占优先级为0-1,用1个bit表示抢占优先级,其余3个bit表示子优先级...

(1)通过优先级分组,可以管理中断的响应顺序

(2)只有抢占优先级才由抢占中断权限,发生中断嵌套,打断就发生中断嵌套,没有能力打断,那就被挂起

假如事件A抢占优先级为0,B的抢占优先级为10,在B执行过程中,A发出中断请求,则会抢过B中断的使用权,等A执行完毕再继续执行B(ps:如果A并不能打断事件B,A就会被挂起)

(3)如果中断抢占优先级相同,不发生抢占

(4)如果多个挂起的中断具有相同的抢占优先级,则子优先级高的先行,如果子优先级相同,则IRQ(通常指外部中断请求)编号小的先行

所以:主优先级>子优先级>IRQ编号 

4.2  实验3:利用外部中断控制LED亮灭(1)(借助其他引脚)

第一步:基础配置(下同)

第二步:配置

第三步:生成代码

第四步:写程序

中断回调函数重写(用串口收发检验是否成功进入函数,Delay尽量不要用在中断中,容易卡,这里待改进)

char buffer1[100]={"第一个中断"};
char buffer2[100]={"第二个中断"};
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)//重写中断回调函数
{
	if(GPIO_Pin==GPIO_PIN_1)
	{
		HAL_UART_Transmit_DMA(&huart1,(unsigned char*)buffer1,100);//检验是否进入函数
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
		HAL_Delay(1000);//延时一秒
		HAL_NVIC_SystemReset();//复位函数
	}
	if(GPIO_Pin==GPIO_PIN_2)
	{
		HAL_UART_Transmit_DMA(&huart1,(unsigned char*)buffer2,100);
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
		HAL_Delay(1000);
		HAL_NVIC_SystemReset();
	}
}

中断服务函数

while (1)
  {
    /* USER CODE END WHILE */
		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==GPIO_PIN_RESET)
		{
			HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);//中断服务函数
		}
		if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
		{
			HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
		}
    /* USER CODE BEGIN 3 */
 
  }

 第五步:编译+写入

4.3  实验3:利用外部中断控制LED亮灭(2)(按键引脚)

第一步

第二步 :配置

这里按键仍然选择上拉输入(原因同上)

中断触发模式选择上升沿/下降沿触发

第三步

第四步:写程序

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_SET)
	{
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
	}
	if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==GPIO_PIN_RESET)
	{
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
	}
}
/* USER CODE END 0 */

while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
		HAL_Delay(5000);
		HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
		HAL_Delay(5000);
  }

第五步 

5  UART/USART

5.1  通讯分类

比特率:单位时间内传输了多少位(比特),单位是bit/s

波特率:单位时间内传输了多少个符号。

波特率与比特率的关系为:比特率=波特率X单个调制状态对应的二进制位数。

              I=S*log2(N)

 5.2  USART

CubeMX:

Baud Rate:波特率

Word Length:字长

Parity:校验位

Stop Bits:停止位

串口调试助手:

 

5.3  常用函数

HAL_UART_Init(&huart1);//初始化串口1函数
HAL_UART_MspInit(&huart1);//串口1回调函数,主要进行硬件部分的初始化
HAL_UART_Transmit(&huart1, uint8_t *pData, uint16_t Size, uint32_t Timeout);//串口1发送数据函数
HAL_UART_Receive(&huart1, uint8_t *pData, uint16_t Size, uint32_t Timeout);//串口1接收数据函数
HAL_UART_Transmit_IT(&huart1, uint8_t *pData, uint16_t Size);//串口1中断方式发送数据
HAL_UART_Receive_IT(&huart1, uint8_t *pData, uint16_t Size);//串口1中断方式接受数据
HAL_UART_Transmit_DMA(&UART1_Handler, uint8_t *pData, uint16_t Size);//DMA方式发送数据
HAL_UART_Receive_DMA(&UART1_Handler, uint8_t *pData, uint16_t Size);//DMA接收数据
HAL_UART_DMAPause(&UART1_Handler);//暂停DMA数据传输
HAL_UART_DMAResume(&UART1_Handler);//从暂停状态中恢复DMA传送
HAL_UART_DMAStop(&UART1_Handler);//停止DMA的传输
HAL_UART_IRQHandler(&huart1);//串口1中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//串口发送 数据完毕的回调函数,当串口使用中断模式发送完毕后才能自动调用本函数。
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);//发送数据一半回调函数
HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);//串口收发数据出错时的回调函数。
HAL_LIN_SendBreak(UART_HandleTypeDef *huart);//LIN总线通信函数,发送一个断开连接的标识。
HAL_MultiProcessor_EnterMuteMode(UART_HandleTypeDef *huart);//多核处理器进入串口静音模式。

5.4  实验4:利用蓝牙模块实现串口通信

CH340模块(USB转TTL)       HC-05模块

VCC(5V)  ——————————  VCC

     GND   ——————————  GND

     RXD   ——————————  TXD

     TXD   ——————————  RXD
 

最开始,用USB转TTL连接HC-05模块(注意此时需要先按住HC-05模块上面的按钮再上电),这时会进入AT模式(此时的灯应该是间隔比较长时间才灭一次),然后打开串口调试助手,波特率设为38400,接下来进行初始化操作

 

 按照步骤

配置蓝牙模块基本信息

     AT+NAME=HC-05   修改蓝牙模块名称为HC-05

    AT+ROLE=0    蓝牙模式为从模式

   AT+CMODE=1   蓝牙连接模式为任意地址连接模式,也就是说 该模块可以被任意蓝牙设备连接

   AT+PSWD=1234   蓝牙配对密码为1234

   AT+UART=9600,0,0  蓝牙通信串口波特率为9600,停止位1位, 无校验位

 

我们直接把蓝牙模块插到原来USB转TTL的位置上(蓝牙模块的用处就是可以无线调试而不用电脑上的虚拟串口调试) (注意不要按按钮)

我们在手机上安装蓝牙串口调试软件,并配对(注意在这之前手机的设置->蓝牙里已经连接成功)

然后这时会发现灯处于长灭状态(偶尔亮一会),就可以正常使用了,我们在手机里输入数据,跟原来用电脑的虚拟串口一模一样

5.5  printf/scanf的重定向

int fputc(int c,FILE *stream)//printf
{
	uint8_t ch[1]={c};
	HAL_UART_Transmit(&huart1,ch,1,0xFFFF);
	return c;
}

int fgetc(FILE *stream)//scanf
{
	uint8_t ch[1];
	HAL_UART_Receive(&huart1,ch,1,0xFFFF);
	return ch[0];
}

6  DMA

6.1  特性及原理

DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

DMA定义:
DMA用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

DMA传输方式
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:

外设到内存
内存到外设
内存到内存
外设到外设


总结:DMA就是CPU的帮手

首先需要建立传输通道(存储器->存储器、存储器->外设、外设->外设)

其次确定传输对象(UART(源)->内存(目标)、内存(目标)->UART(源))

最后敲定传输细节(确定由谁来产生DMA请求、通道优先级、确定传输双方的数据格式、确定数据是否需要循环采集(循环模式是否使能)、是否需要传输标志/中断)

 6.2  FIFO

FIFO(First In First Out,即先入先出),是一种数据缓冲器。先被写入的数据会按顺序先被读出。FIFO可看做一个管道,有数据写入端口和 数据读取端口:

如图,数据写入端口从1~10依次写入数据,则数据读取端口也从1~10依次读取数据。输出端口每读出一位数据,FIFO中的后一位数据就向前移一位。如读取端口读出1、2、3后FIFO输出端口的第一位变为4。

6.3  传输、工作模式 

6.3.1   传输模式:单次传输与突发传输

单次传输模式下,一次操作(软件)只能传输一次(一次可以理解为一个节拍,如一个字节)

突发传输模式下,一次操作可以传输多次,如4次,8次,16次(利用到FIFO)

6.3.2  工作模式:FIFO模式与直接模式

FIFO模式下,可以将要传输的多个数据(或字节)累计存储在FIFO缓冲器中,然后在FIFO缓冲器中设置存储阈值,当到达阈值时,FIFO会自动把所有存储的数据一次性的发送到目标地址;

直接模式下,DMA直接进行数据从源地址到目的地址的传输,对于外设的传输,因为外设内部一般也有FIFO,所以传输的数据可以被直接存储在外设的FIFO中。

6.4  实验5:利用空闲中断+串口DMA实现不定长收发

第一步

第二步:配置

第三步

第四步:写程序

while循环外

HAL_UART_Receive_DMA(&huart1,buffer,3);
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);

 在it.c文件中找到串口中断服务函数

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE)!=RESET)
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);
		HAL_UART_DMAStop(&huart1);
		len=3-__HAL_DMA_GET_COUNTER(huart1.hdmarx);
		HAL_UART_Transmit_DMA(&huart1,buffer,len);
		HAL_UART_Receive_DMA(&huart1,buffer,3);
	}
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

第五步 

7  HAL库外设驱动原理

7.1  句柄结构体(xx_HandleTypeDef)

Instance:它指向了外设内,一个具体的外设成员。如:ADC里的ADC1、ADC2,UART里的UART1、UART2,DMA里的Channel1、Channel2,实际上他用指针指向一个外设基地址

Init:指向了一个具体外设的初始化结构体用来配置外设的工作参数

7.2  初始化结构体(xx_InitTypeDef)

8  定时器

8.1  基础知识

 递增模式:

递减模式: 

中心对齐模式: 

 

8.2  PWM

8.3  相关函数

 8.3.1  定时器基本函数

/* Time Base functions ********************************************************/
HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_DeInit(TIM_HandleTypeDef *htim);
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim);
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim);
/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_TIM_Base_Start_DMA(TIM_HandleTypeDef *htim, uint32_t *pData, uint16_t Length);
HAL_StatusTypeDef HAL_TIM_Base_Stop_DMA(TIM_HandleTypeDef *htim);
HAL_TIM_PeriodElapsedCallback();//中断回调函数

8.3.2  PWM

/* Timer PWM functions *********************************************************/
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_PWM_DeInit(TIM_HandleTypeDef *htim);
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim);
void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim);
/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

8.4  实验6:定时器输入捕获实验

第一步

第二步:配置

第三步:生成代码

第四步:写程序

 定义变量

uint8_t CaptureSta=0;//输入捕获状态
uint16_t CaptureVal=0;//输入捕获值

定时器输入捕获中断处理回调函数

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)//确定中断的是TIM2
	{
		if((CaptureSta&0x80)==0)//还没有成功捕获
		{
			if(CaptureSta&0x40)//捕获到一个下降沿
			{
				CaptureSta|=0x80;//标记成功捕获到一次高电平脉冲
				CaptureVal=HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);//获得捕获值
				TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1);//一定要重新设置
				TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//
			}
			else
			{
				CaptureSta=0;
				CaptureVal=0;
				CaptureSta|=0x40;
				__HAL_TIM_DISABLE(&htim2);
				__HAL_TIM_SET_COUNTER(&htim2,TIM_CHANNEL_1);
				TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1);
				TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
				__HAL_TIM_ENABLE(&htim2);
			}
		}
	}
}

定时器溢出中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{
		if((CaptureSta&0x80)==0)
		{
			if(CaptureSta&0x40)
			{
				if((CaptureSta&0x3F)==0x3F)
				{
					TIM_RESET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1);
					TIM_SET_CAPTUREPOLARITY(&htim2,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
					CaptureSta|=0X80;
					CaptureVal=0xFFFF;
				}
				else
				{
					CaptureSta++;
				}
			}
		}
	}
}

printf重定向

int fputc(int c,FILE *stream)//printf
{
	uint8_t ch[1]={c};
	HAL_UART_Transmit(&huart1,ch,1,0xFFFF);
	return c;
}

main函数 

while (1)
  {
    /* USER CODE END WHILE */
		if(CaptureSta&0x80)//成功捕获一次高电平
		{
			int temp=CaptureSta&0x3F;
			temp*=1000000;//溢出时间总和
			temp+=CaptureVal;//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);
			CaptureSta=0;//开启下一次捕获
		}
		int t;
		t++;
		if(t>20)//检验程序正常进行
		{
			t=0;
			HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_9);
		}
		HAL_Delay(10);
    /* USER CODE BEGIN 3 */
  
  /* USER CODE END 3 */
	}

第五步:编译+写入

8.5  实验7:舵机驱动

 我们打开TIM3定时器,这个定时器是挂在APB1总线上的,根据配置好的时钟树可知

频率是84MHz 

然后为了方便计算,我们把分频系数设置成84-1,这样相当于1秒钟点1000000次

由于舵机的驱动信号是50Hz,周期是0.02s,所以在一个周期内会点20000次

舵机的转动角度是按照高电平的占空比来计算的

 

经过手算,我们可以大致推出角度和比较值之间的关系,舵机的难点也就在这了

舵机                  单片机

信号线(黄)-------PA6(根据自己的定时器而定)

电源线(红)-------ST-LINK上的5V(9/10)

地线(棕)----------GND

9  I2C

9.1  简介

10  SPI

10.1  简介

 

 

.. 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/353000.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习|ROC曲线和AUC值

概念AUC&#xff08;Area Under Curve&#xff09;被定义为ROC曲线下的面积。其中&#xff0c;ROC曲线全称为受试者工作特征曲线 &#xff08;receiver operating characteristic curve&#xff09;&#xff0c; 模型会计算出所判断事物为汉堡&#x1f354;的概率&#xff0c;而…

【游戏客户端开发的进阶路线】

*** 游戏客户端开发的进阶路线 春招的脚步越来越近&#xff0c;我们注意到越来越多的同学们都在积极学习游戏开发&#xff0c;希望能在这个充满活力的行业中大展拳脚。 当我们思考如何成为游戏开发领域的佼佼者时&#xff0c;关键在于如何有效规划学习路径。 &#x1f914; 我…

11.Elasticsearch应用(十一)

Elasticsearch应用&#xff08;十一&#xff09; 1.什么是自动补全 现代的搜索引擎&#xff0c;一般都会提供Suggest as you type的功能 帮助用户在输入搜索的过程中&#xff0c;进行自动补全或者纠错。通过协助用户输入更加精准的关键词&#xff0c;提高后续搜索阶段文档的…

看图说话:Git图谱解读

很多新加入公司的同学在使用Git各类客户端管理代码的过程中对于Git图谱解读不太理解&#xff0c;我们常用的Git客户端是SourceTree&#xff0c;配合P4Merge进行冲突解决基本可以满足日常工作大部分需要。不同的Git客户端工具对图谱展示会有些许差异&#xff0c;以下是SourceTre…

【教程】MobaXterm软件Keygen快速生成注册码

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 1、去官网安装正版软件&#xff0c;比如23.6版本的&#xff1a;MobaXterm free Xserver and tabbed SSH client for Windows 2、打开这个网站&#xff0c;输入信息&#xff1a;MobaXterm Keygen 3、将自动下载的C…

【原理图PCB专题】Allegro报封装Name is too long

在安装完成Cadence17.4版本后&#xff0c;在首次导入网表时发现PCB报了一些错误&#xff0c;就是名称太长 #1 ERROR(SPMHNI-189): Name is too long… ERROR(SPMHNI-189): Problems with the name of device ‘MT48LC2M32B2B5-6_SDRAMTSOP86_MT48LC2M32B2B5-6’: ‘Name is to…

vue-component组件

一、Component 组件 组件&#xff08;Component&#xff09;是自定义封装的功能。在前端开发过程中&#xff0c;经常出现多个网页的功能是重复的&#xff0c;而且很多不同的页面之间&#xff0c;也存在同样的功能。将相同的功能进行抽取,封装为组件,这样&#xff0c;前端人员就…

JavaWeb,Vue的学习(上)

概述 Vue的两个核心功能 声明式渲染&#xff1a;Vue 基于标准 HTML 拓展了一套模板语法&#xff0c;使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。响应性&#xff1a;Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM ViteVue3项目…

统计学-R语言-8.2

文章目录 前言双因子方差分析数学模型主效应分析交互效应分析正态性检验 绘制3个品种产量数据合并后的正态Q-Q图&#xff08;数据&#xff1a;example8_2&#xff09;练习 前言 本篇将继续介绍方差分析的知识。 双因子方差分析 考虑两个类别自变量对数值因变量影响的方差分析…

elasticsearch在ubuntu下的配置以及简单使用

参考资料 官方下载地址 ELK学习实验002&#xff1a;Elasticsearch介绍及单机安装 ElasticSearch (ES从入门到精通一篇就够了) 前言 警告&#xff1a;elasticsearch默认不允许使用root账号来运行的&#xff0c;所以&#xff0c;强烈建议一开始就创建一个账号例如&#xff1a;…

HarmonyOS4.0系统性深入开发28线性布局

线性布局&#xff08;Row/Column&#xff09; 概述 线性布局&#xff08;LinearLayout&#xff09;是开发中最常用的布局&#xff0c;通过线性容器Row和Column构建。线性布局是其他布局的基础&#xff0c;其子元素在线性方向上&#xff08;水平方向和垂直方向&#xff09;依次…

大数据安全 | 期末复习(中)

文章目录 &#x1f4da;感知数据安全⭐️&#x1f407;传感器概述&#x1f407;传感器的静态特性&#x1f407;调制方式&#x1f407;换能攻击&#x1f407;现有防护策略 &#x1f4da;AI安全⭐️&#x1f407;智能语音系统——脆弱性&#x1f407;攻击手段&#x1f407;AI的两…

CUDA编程- - GPU线程的理解 thread,block,grid - 再次学习

GPU线程的理解 thread,block,grid 一、从 cpu 多线程角度理解 gpu 多线程1、cpu 多线程并行加速2、gpu多线程并行加速2.1、cpu 线程与 gpu 线程的理解&#xff08;核函数&#xff09;2.1.1 、第一步&#xff1a;编写核函数2.1.2、第二步&#xff1a;调用核函数&#xff08;使用…

使用多时相的遥感影像进行自动分类的开源模型

简介 Prithvi是NASA 和 IBM 创建的一个用于遥感分类的开源基础模型&#xff0c;使用Landsat 和 Sentinel-2 ( HLS ) 数据集进行训练&#xff0c;特别适合处理时间序列的遥感影像。 该模型已经在2023年的8开源和部署到了Hugging Face Hub上&#xff0c;用户可以免费下载和在线…

【Godot4自学手册】第七节背景搭建

各位同学&#xff0c;今天是第七节&#xff0c;在本节我会学习如何使用TileMap来完成背景搭建。 一、添加TileMap结点 先做个介绍&#xff0c;TileMap是基于 2D 图块的地图节点。Tilemap&#xff08;图块地图&#xff09;使用 TileSet&#xff0c;其中包含了图块的列表&#…

移动Web——平面转换-旋转

1、平面转换-旋转 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style…

asp.net宠物流浪救助系统

asp.net宠物流浪救助系统 当领养人是无或者未领养的时候&#xff0c;就会显示领养申请按钮&#xff0c;登陆的用户可以申请领域该宠物&#xff0c;未登录会提示登陆然后转到登陆页面 宠物领养页面支持关键字查询符合条件的宠物 当有领养人时就隐藏领养申请按钮 社区交流意见…

嵌入式linux面试题目总结

Linux系统中常见的面试题目&#xff0c;分享&#xff0c;欢迎大家前来交流学习。 1、嵌入式系统中的CAN通信协议是什么&#xff1f; CAN&#xff08;Controller Area Network&#xff09;通信协议是一种广泛应用于嵌入式系统中的串行通信协议。它最初由德国汽车工业联合会开发…

LabVIEW信号时间间隔测量

用LabVIEW软件平台开发一个用于测量两路信号时间间隔的系统。系统利用LabVIEW的数据采集和处理能力&#xff0c;能够准确测量并分析来自不同硬件板卡的信号时间间隔&#xff0c;这对于精确控制和数据分析至关重要。 系统主要由以下几部分组成&#xff1a;数据采集卡、信号处理…

JasperReports渲染报表文件时候,读取图片报错:Byte data not found at:xxx.png.\r\n\tat

目录【知识星球】 1.1、错误描述 1.2、解决方案 1.1、错误描述 最近在工作中&#xff0c;使用JasperReports报表比较多一些&#xff0c;有次线上环境里面运行报错&#xff0c;查看报错日志&#xff0c;如下所示&#xff1a; net.sf.jasperreports.engine.JRException: Byte…