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  简介

 

 

.. 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis常见数据类型[上]

目录 前言&#xff1a; 基本全局命令 KEYS EXISTS DEL EXPIRE TTL TYPE 数据结构和内部编码 内部编码&#xff1a; 单线程架构 引出单线程模型&#xff1a; 为什么单线程还这么快&#xff1f; String字符串 字符串数据类型&#xff1a; 常见命令&#xff1a; S…

计算机网络-编制与调制(基带信号 基带传输 宽度信号 宽度传输 编码 调制 )

文章目录 基带信号与宽带信号编码与调制数字数据编码为数字信号数字数据调制为模拟信号模拟数据编码为数字信号模拟数据调制为模拟信号小结 基带信号与宽带信号 信道上传输的信号除了可以分为数字信号和模拟信号&#xff0c;也可以分为基带信号和宽带信号&#xff0c;只是分类…

文心一言 VS 讯飞星火 VS chatgpt (188)-- 算法导论14.1 5题

五、用go语言&#xff0c;给定 n 个元素的顺序统计树中的一个元素 x 和一个自然数 i &#xff0c;如何在O(lgn)的时间内确定工在该树线性序中的第 i 个后继&#xff1f; 文心一言&#xff0c;代码正常运行&#xff1a; 在顺序统计树&#xff08;也称为平衡二叉搜索树&#xf…

2. HarmonyOS 应用开发 DevEco Studio 准备-2

2. HarmonyOS 应用开发 DevEco Studio 准备-2 首选项设置 中文设置 主题 字体 插件安装和使用 保存时操作 编辑器 工程树管理 代码树管理 标记 字符串可视化编辑 参考文档 常用快捷键 编辑 查找或替换 编译与运行 调试 其他 预览 页面预览 自定义组件预览 预览…

[设计模式Java实现附plantuml源码~创建型] 复杂对象的组装与创建——建造者模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

linux中配置文件目录为什么用etc来命名

在早期的 Unix 系统中&#xff0c;/etc 目录的名称确实来源于单词 “etcetera” 的缩写&#xff0c;最初意味着 “其他”&#xff0c;用来存放杂项或者不属于其他特定目录的文件。然而&#xff0c;随着时间的推移&#xff0c;/etc 目录的用途逐渐演变并专门化。 在现代的 Linux…

数据监控-Prometheus/Grafana

一、数据监控Prometheus 1、什么是Prometheus Prometheus是由SoundCloud开源监控告警解决方案,从2012年开始编写代码,到2015年github上开源以来,吸引不少用户以及公司的使用。Prometheus作为新一代的开源解决方案,很多理念与Google SRE的运维之道不谋而合。 2、Promet…

Chrome单独配置代理的方法

Windows Windows上单独对Chrome设置代理&#xff0c;需要在启动时传递参数&#xff0c;具体步骤如下。 在Chrome浏览器的快捷方式上右击&#xff0c;进入属性。在 快捷方式 标签下找到 目标 项目&#xff0c;在最后添加 –proxy-server“socks5://xxx.xxx.xx.xx:xxxx” 如果要…

微信小程序之页面导航、生命周期和WXS脚本

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

架构篇21:高性能负载均衡-算法

文章目录 轮询加权轮询负载最低优先性能最优类Hash 类源地址 HashID Hash小结负载均衡算法数量较多,而且可以根据一些业务特性进行定制开发,抛开细节上的差异,根据算法期望达到的目的,大体上可以分为下面几类。 任务平分类:负载均衡系统将收到的任务平均分配给服务器进行处…

新建一个基于标准库的工程(STM32)

目录 1.新建存放工程的文件夹 2.打开KEIL5软件 3.新建一个本次工程的文件夹 4.添加工程的必要文件 4.1打开STM32的启动文件 ​编辑 4.2&#xff1a; 4.3添加内核寄存器文件 ​编辑 5.回到keil5软件&#xff0c;将刚才复制的那些文件添加到工程中 5.1添加一个启动文件&am…

使用Spring Boot和Tess4J实现本地与远程图片的文字识别

概要&#xff1a; 在本文中&#xff0c;我们将探讨如何在Spring Boot应用程序里集成Tess4J来实现OCR&#xff08;光学字符识别&#xff09;&#xff0c;以识别出本地和远程图片中的文字。我们将从添加依赖说起&#xff0c;然后创建服务类以实现OCR&#xff0c;最后展示如何处理…

【笔试常见编程题01】删除公共字符串、组队竞赛、倒置字符串、排序子序列

1. 删除公共字符串 输入两个字符串&#xff0c;从第一字符串中删除第二个字符串中所有的字符。 例如&#xff0c;输入”They are students.”和”aeiou”&#xff0c;则删除之后的第一个字符串变成”Thy r stdnts.” 输入描述 每个测试输入包含2个字符串 输出描述 输出删除后的…

OpenCV 0 - VS2019配置OpenCV

1 配置好环境变量 根据自己的opencv的安装目录配置 2 新建一个空项目 3 打开 视图->工具栏->属性管理器 4 添加新项目属性表 右键项目名(我这是opencvdemo)添加新项目属性表,如果有配置好了的属性表选添加现有属性表 5 双击选中Debug|x64的刚添加的属性表 6 (重点)添…

Vue3 Teleport 将组件传送到外层DOM位置

✨ 专栏介绍 在当今Web开发领域中&#xff0c;构建交互性强、可复用且易于维护的用户界面是至关重要的。而Vue.js作为一款现代化且流行的JavaScript框架&#xff0c;正是为了满足这些需求而诞生。它采用了MVVM架构模式&#xff0c;并通过数据驱动和组件化的方式&#xff0c;使…

nginx反向代理负载均衡

一&#xff0c;kali作为负载服务器 打开kali nginx服务&#xff0c;访问页面如下 使用docker拉取nginx&#xff0c;并做出端口映射 ┌──(root?kali)-[/etc/nginx] └─# docker pull nginx ┌──(root㉿kali)-[/etc/nginx] └─# docker run -p 11111:80 --name Jdr -d ng…

Type-C平板接口协议芯片介绍,实现单C口充放电功能

在现代平板电脑中&#xff0c;Type-C接口已经成为了一个非常常见的接口类型。相比于传统的USB接口&#xff0c;Type-C接口具有更小的体积、更快的传输速度和更方便的插拔体验。但是&#xff0c;在使用Type-C接口的平板电脑上&#xff0c;如何实现单C口充电、放电和USB2.0数据传…

首发:2024全球DAO组织发展研究

作者&#xff0c;张群&#xff08;专注DAO及区块链应用研究&#xff0c;赛联区块链教育首席讲师&#xff0c;工信部赛迪特邀资深专家&#xff0c;CSDN认证业界专家&#xff0c;微软认证专家&#xff0c;多家企业区块链产品顾问&#xff09; DAO&#xff08;去中心化自治组织&am…

HCS 华为云Stack产品组件

HCS 华为云Stack产品组件 Cloud Provisioning Service(CPS) 负责laas的云平台层的部署和升级是laas层中真正面向硬件设备&#xff0c;并将其池化软件化的部件。 Service OM 资源池(计算/存储/网络)以及基础云服务(ECS/EVS/PC)的管理工具。 ManageOne ManageOne包括服务中心…

【MySQL】学习如何通过DML更新数据库的数据

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-QIqURn9fNFMjLD9l {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…