锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

STM32基础课程笔记

时间:2023-05-22 17:37:00 v8三极管三极管bc546to

【1】STM32简介

  1. 命名规范
    STM32F051K8U6
    ST - 公司
    M - micro eletronics
    32 - 32位处理器 STM8
    F - 基础版 L - 低功耗 G - 电源
    051 - 入门级别 103 - 主流级别 407 - 高性能
    K - 32个管脚
    8 - 64K
    U - UQFN 封装类型
    6 - 工作温度范围

  2. STM32ARM的关系?
    STM32CPU是采用的ARM-CortexM0架构的

ARM处理器命名规范
ARM7\9\11
Cortex - A : 开放式操作系统 手机 智能电视
Cortex - R : 实时系统 汽车电子
Cortex - M : 低成本优化解决方案

实时操作系统:一般用于单片机
分时操作系统:通过时间片轮转进行调度 Linux典型的分时操作系统
最大的区别是响应速度。

处理器和架构?
三星S5P6818 ARM-cortexA53 v8
麒麟990 4ARM-cortexA55 4ARM-cortexA76 v8

【2】Cortex-M0架构

Cortex-M0平衡主要功耗和性能
Cortex-M0
微处理器主要包括处理器内核、嵌套向量中断控制器(NVIC)、通过高性能总线组成调试子系统和内部总线系统(AHB-LITE)与外部通信。

两种工作模式:

  1. 线程模式(Thread Mode):芯片复位后,即进入线程模式,执行用户程序;
  2. 处理模式(Handler Mode):当处理器出现异常或中断时,进入处理模式并返回线程模式

两种工作状态:

  1. Thumb状态:正常运行时处理器的状态
  2. 调试状态:处理器的状态

相关寄存器:

  1. R0-R12 通用寄存器13个
  2. R13SP栈指针):Cortex-M0不同物理位置有两个栈指针,主栈指针 MSP,进程栈指针PSP。在处理模式下,只能使用主堆栈,主堆栈也可以在线程模式下使用,主要由 CONTROL完成寄存器控制。系统上电的默认栈指针是MSP
  3. R14LR链接寄存器):程序跳转时保存正在执行的下一个指令地址。
  4. R15PC程序计数器):存储下一个指令的地址。
  5. 特殊寄存器:
    1. xPSR:该寄存器由三个程序状态寄存器组成
    2. 应用PSRAPSR):包括前一条指令执行后的条件标志
    3. 中断PSRIPSR):包含当前ISR的异常编号
    4. 执行PSREPSR):包含Thumb状态位

CortexM0支持的异常和中断:

Cortex-M0最多支持处理器 32 外部中断(通常称为外部中断(通常称为 IRQ)和一个不可阻挡的中断(NMI),
另外 Cortex-M0 还支持多种系统异常(Reset、HardFault、SVCall、PendSV、SysTick

指令集:

  1. ARM指令集
    32位精简指令集;
    指令长度固定;
    减少编码量的消耗,减轻解码和装配线的负担;

  2. Thumb指令集
    Thumb指令集是ARM指令集的子集;
    指令宽度16位;
    与32位指令集相比,系统的存储空间大大节省;
    Thumb指令集不完整,必须配合ARM指令集一起使用。

【3】CortexM0寄存器映射

  1. 寻址空间 0-4G
    32位 2^32次方
    4G = 4 * 1024 * 1024 * 1024 = 4,294,967,296
  2. 寄存器地址映射
    在编程手册中找到GPIO相关寄存器,GPIOA起始地址为0x28000000
    MODER 0x00
    OTYPER 0x04
#define GPIOA_BASE ((usigned int)0x28000000)  #define GPIOA_OTYPER *(unisgned int *)(GPIOA_BASE 0x04) 

封装成结构:

typedef struct { 
           __IO uint32_t MODER;        /*!< GPIO port mode register, Address offset: 0x00 */   __IO uint32_t OTYPER;       /*!< GPIO port output type register, Address offset: 0x04 */   __IO uint32_t OSPEEDR;      /*!< GPIO port output speed register, Address offset: 0x08 */
  __IO uint32_t PUPDR;        /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */
  __IO uint32_t IDR;          /*!< GPIO port input data register, Address offset: 0x10 */
  __IO uint32_t ODR;          /*!< GPIO port output data register, Address offset: 0x14 */
  __IO uint32_t BSRR;         /*!< GPIO port bit set/reset register, Address offset: 0x1A */
  __IO uint32_t LCKR;         /*!< GPIO port configuration lock register, Address offset: 0x1C */
  __IO uint32_t AFR[2];       /*!< GPIO alternate function low register, Address offset: 0x20-0x24 */
  __IO uint32_t BRR;          /*!< GPIO bit reset register, Address offset: 0x28 */
} GPIO_TypeDef;


#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
GPIOA->OTYPER = 0X20;

位操作赋值:

  • 清零用与,置一用或。
    GPIOA->OTYPER的第6位置1,第7位置0。
    先清零后置位:
  GPIOA->OTYPER = GPIOA->OTYPER & ~(0x3 << 6) | (1<<6) 

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOA_BASE (AHB2PERIPH_BASE + 0x00000000UL)
#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL)
#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */
  • 实际GPIOA的基地址为0x40000000+ 0x08000000 = 0x48000000

【4】启动文件

Stack_Size		EQU     0x400  //初始化栈空间为1K

            AREA    STACK, NOINIT, READWRITE, ALIGN=3  //定义一个段STACK,未初始化,可读可写,ALIGN=3 以2^3 = 8字节对齐
Stack_Mem       SPACE   Stack_Size
__initial_sp

Heap_Size      EQU     0x200  //初始化堆空间为512字节

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

【实验】通过寄存器点亮LED灯

  • ​ 点亮D10绿灯 - LED4 - 核心板PB0
    ​ 让PB0这个管脚输出低电平即可
  1. 使能GPIOB的时钟
    ​ 将RCCAHBENR的第18位置1
    RCC->AHBER |= 1<<18;
  2. 配置PB0为输出模式
    ​ 将GPIOBMODER的第0位置1,第1位清零。
    GPIOB->MODER &= ~(0x3);
    GPIOB->MODER |= 0x1;
  3. 配置PB0为推挽输出
    ​ 将GPIOBOTYPER的第0位清零;
    GPIOB-> OTYPER &= ~0x1
  4. ​ 将GPIOBODR的第0位清零
    GPIOB->ODR &= ~0x1

【5】GPIO 通用输入输出口

1.输出类型

三极管(流控)
放大状态:发射结(BE)正偏,集电结(BC)反偏。
饱和状态:发射结正偏,集电结正偏。
截止状态:发射结反偏,集电结反偏。

推挽输出:具备输出高低电平的能力。
开漏输出:具备输出低电平的能力,可通过外加上拉电阻输出高电平。

2.输入类型

浮空输入 :IO -> 施密特触发器 -> 输入寄存器 -> 读
模拟输入 : IO -> 输入寄存器 -> 读
上拉输入 : IO -> 上拉电阻 -> 施密特触发器 -> 输入寄存器 -> 读
下拉输入 :IO -> 下拉电阻 -> 施密特触发器 -> 输入寄存器 -> 读

3.GPIO相关寄存器

  1. 4个32位配置寄存器
    1. GPIOx_MODER模式寄存器
    2. GPIOx_OTYPER输出类型寄存器
    3. GPIOx_OSPEEDR输出速度寄存器
    4. GPIOx_PUPDR上拉下拉寄存器
      浮空取决于外设的电平
      上拉 - 高
      下拉 - 低
  2. 2个32位数据寄存器
    1. GPIOx_IDR输入数据寄存器
    2. GPIOx_ODR输出数据寄存器
  3. 1个32位 置位/复位寄存器
    GPIOx_BSRR
  4. 2个32位 复用功能配置寄存器
    1. GPIOx_AFRH复用功能高位寄存器
    2. GPIOx_AFRL 复用功能低位寄存器

【实验】点亮一个LED灯

  1. 查看电路原理图
    在底板原理图上找到对应的LED灯 LED2 LED3 LED4
    在核心板原理图上找到对应控制管脚 PB2 PB1 PB0
  2. 查看芯片手册找到对应的控制寄存器
    1. 开启GPIOB的时钟
      RCC_AHBENR
      位 18 IOPBEN: GPIOB 时钟使能
      由软件置 1 或清 0.
      0: GPIOB 时钟关闭
      1: GPIOB 时钟开启
      RCC->AHBENR |= 1<<18

    2. 选择输出模式
      GPIOB_MODER 010101
      MODERy[1:0]: 端口 x 配置位 (y = 0…15)
      这些位可由软件写来配置 I/O 口模式。
      00: 输入模式 ( 复位状态 )
      01: 通用输出模式
      10: 复用功能模式
      11: 模拟模式
      GPIOB->MODER |= (1<<0)|(1<<2)|(1<<4)

    3. 选择输出类型
      GPIOx_OTYPER
      OTy[1:0]: 端口 x 的配置位 (y = 0…15)
      这些位可由软件写来配置 I/O 口的输出类型。
      0: 推挽输出 ( 复位状态 )
      1: 开漏输出
      GPIOB->OTYPER = 0x0;

    4. 配置输出数据
      GPIOB_BSRR 端口置位 / 复位寄存器
      位 31:16 BRy: 端口 x 复位位 y(y = 0…15)
      这些位只写。读这些位时返回 0x0000 数值。
      0: 对相应的 ODRx 位无影响
      1: 复位相应的 ODRx 位
      GPIOB -> BSRR = (0x7 << 16);

  
 //LED灯初始化
 void LED_Init()
 { 
        
     RCC->AHBENR |= 1<<18;                 //使能GPIOB的时钟
 	 GPIOB->MODER |= (1<<0)|(1<<2)|(1<<4); //配置PB0为输出模式
     GPIOB->OTYPER = 0x0;                  //配置为推挽输出
 }
 void main()
{ 
        
   while (1)
   { 
        
     //实现三个LED灯闪烁
   	 GPIOB -> BSRR = (0x7 << 16);//复位PB0 PB1 PB2输出低电平
  	 HAL_Delay(500);
  	 GPIOB -> BSRR = 0x7;       //置位PB0 PB1 PB2 输出高电平
  	 HAL_Delay(500);
   }
}

【6】HAL库编程版本

  1. GPIO写函数
    void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
    功能:给定GPIO引脚写入指定数据
    参数:
  GPIO_TypeDef* GPIOx 端口 A...F
  uint16_t GPIO_Pin  引脚编号 0 - 15
  GPIO_PinState PinState
  GPIO_PIN_RESET:0  低电平
  GPIO_PIN_SET:1  高电平
	 返回值:空
	 
void MX_GPIO_Init(void)  //LED初始化函数
{ 
        

  GPIO_InitTypeDef GPIO_InitStruct = { 
        0};
   
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();  //使能GPIOB的时钟

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2, GPIO_PIN_RESET);
  //配置PB0 PB1 PB2 初始状态为输出低电平

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  //配置输出模式为推挽输出
  GPIO_InitStruct.Pull = GPIO_NOPULL;  //无上拉下拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;  //输出速度为低速
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //使能GPIOB组

}
  • HAL库实现流水灯:
 	 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0|GPIO_PIN_2,GPIO_PIN_SET);
     HAL_Delay(200);
     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_2,GPIO_PIN_RESET);
     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0|GPIO_PIN_1,GPIO_PIN_SET);
     HAL_Delay(200);
     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
     HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1|GPIO_PIN_2,GPIO_PIN_SET);
     HAL_Delay(200);

【实验】 五向按键控制实验

  1. 五向按键通过一个或门由 D3&KEY输出
    只要有一个方向按键按下,则能够从D3&KEY这个管脚读到高电平
  2. 核心板PA8管脚连接D3&KEY
    PA8管脚输入的信号即可判断按键是否被按下
  3. PA8管脚的值

HAL_GPIO_ReadPin
GPIO_PinState HAL_GPIO_ReadPin (GPIO_TypeDef * GPIOx,uint16_t GPIO_Pin)
功能:读管脚的电平值
参数:GPIOx端口号
GPIO_Pin 管脚编号
返回值:GPIO_PinState管脚实际的电平值

if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == 1)
{ 
            		  
     HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,0);
}else
{ 
        
     HAL_GPIO_WritePin(LED4_GPIO_Port,LED4_Pin,1);
}
HAL_Delay(100);

void HAL_GPIO_TogglePin (GPIO_TypeDef * GPIOx, uint16_tGPIO_Pin)
功能:电平翻转
参数:GPIOx 端口号
GPIO_Pin管脚编号
返回值:空

   
  //按键按一次 LED切换一次状态
  if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == 1)
  { 
             		  
	  while(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin) == 1);
	  //抬手监测
      HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);				    
  }

【7】通信相关基础知识

  • 通信对象最少要有两个
    1. 根据时钟源可以区分为:

      1. 同步通信:通信双方共用一个时钟信号。
      2. 异步通信:通信双方各自都有一个独立时钟源,双方约定好通信速度。
    2. 通过通信方式区分为:

      1. 串行通信:用一根数据线进行通信,同一时刻只接收一个bit位数据
        优点:节省资源,占用管脚少
        缺点:通信效率低
      2. 并行通信:用多根信号线同时进行同时,同一时刻能够接收多个bit位数据
        优点:通信效率高
        缺点:占用管脚资源多
    3. 根据传输方向区分为:
      单工:只能作为接收设备或者发送设备,要么收要么发。 广播、收音机
      半双工:可以接收也可以发送,但是同一时间只能发送或接收。对讲机
      全双工:同一时间既可作为发送端又可作为接收端。手机

【8】串口USART – 通用同步异步收发器

UART – 异步通信 串行 全双工
USART – 同步通信
(5v)TTL电平 : 逻辑1 :2.4v ~ 5v 逻辑0: 0 ~ 0.5v

EIA电平(RS232): 逻辑1 : -3v ~ -15v 逻辑0:+3v ~ +15v 15m
RS485 :双绞线 差分信号 1300m
逻辑1 : 2v ~ 6v 逻辑0:-2v ~ -6v
单片机 — 串口线 — 电脑
CH340
USB转串口芯片

【9】串口通信协议

起始位(1位):低电平
数据位(8位):
校验位(1位):奇偶校验
停止位(1位):高电平
奇校验:数据位中1的个数+校验位上1的个数为奇数
偶校验:数据位中1的个数+校验位上1的个数为偶数
例如:
发送01010101采用奇校验,校验位应该为1

数据接收过程 : RX -> 接收移位寄存器 -> 接收数据寄存器 -> CPU or DMA
数据发送过程 : CPU or DMA -> 发送数据寄存器 -> 发送移位寄存器 —> TX

【10】串口相关寄存器

配置寄存器:
控制寄存器 USART_CR1 CR2
波特率寄存器 USART_BRR

发送数据寄存器:
中断和状态寄存器USART_ISR
接收数据寄存器 USART_RDR
发送数据寄存器USART_TDR

【11】串口发送实验

  1. 分析原理图
    PA9USART1_TX
    PA10USART1_RX
  2. 配置CubeMX
    使能USART1 - 异步通信Asynchronous
    配置USART1参数
    波特率 – 115200
    数据位 –8位
    校验位 – NONE
    停止位 – 1位
  3. 编写发送函数
  void My_Putchar(uint8_t ch)
     { 
        
     	while(!(USART1->ISR & (1<<7)));
        USART1->TDR = ch;
     }

【12】串口接收实验

完成串口接收函数
实现大小写转换
例如:通过串口给单片机发送大写字母A,单片机返回小写字母a。
先实现串口接收函数:

uint8_t My_Getchar(void)
{ 
        
	 uint8_t ch;
	 while(!(USART1->ISR & (1<<5))); //等待接收寄存器非空
	 ch = USART1->RDR;
	 return ch;
}
在main函数里实现:
while(1){
  ch = My_Getchar();
  if(ch>='A'&&ch<='Z')
  {
	 My_Putchar(ch + 32);
  }else if(ch>='a'&&ch<='z')
  {
	 My_Putchar(ch - 32);
  }else
  {
	 My_Putchar('?');
  }
}

【13】HAL库函数版本

  1. HAL库版本发送数据:
    HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
    UART_HandleTypeDef *huart : 句柄 (注意取地址)
    uint8_t *pData : 发送的字符数组首地址
    uint16_t Size: 发送数据长度
    uint32_t Timeout : 超时时间(规定时间内没完成发送返回HAL_TIMEOUT

HAL_UART_Transmit(&huart1, "hello test\n", strlen("hello test\n"), 100);

  1. HAL库版本接收数据:
    HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

  2. 通过HAL库函数实现串口收发:

uint8_t str[32] = { 
        0};
while(1){ 
        
	HAL_UART_Receive(&huart1, str,sizeof(str), 100); 
	HAL_UART_Transmit(&huart1, str, strlen(str), 100);  //用strlen记得添加string.h文件
	memset(str,0,sizeof(str));
}

【13】printf重定向到串口工具实验

a想要发送一个不定长度的到串口
将printf打印的内容输出到串口中,因为printf使用标准库fputc
那么我们将fputc重写即可
添加stdio.h头文件,编译后查找fputc函数 将函数原型找出来
extern _ARMABI int fputc(int /*c*/, FILE * /*stream*/) __attribute__((__nonnull__(2)));
重写fputc函数:

int fputc(int ch, FILE *file)
	{ 
        
		while(!((huart1.Instance->ISR) & (1<<7)));
  		huart1.Instance->TDR = ch;
  		return ch;
	}

007A1200
02DC6C00
在main.c里测试printf:
printf(“hello world\n”);

【13】scanf输入重定向实验

重写fgetc函数

	extern _ARMABI int fgetc(FILE * /*stream*/) __attribute__((__nonnull__(1)));


	int fgetc(FILE * file)
	{ 
        
		int ch;
		while(!((huart1.Instance->ISR) & (1<<5)));
		ch=huart1.Instance->RDR;
		return ch;
	}

main函数里通过scanf和printf实现输入输出:
scanf("%s",buf);
printf("%s",buf);
注意: 在串口工具里发送字符串时记得添加/r/n结束标志 (可参考群里图片设置自动发送附加位)

【14】中断介绍

  1. 概念:当处理器在正常执行程序时,突然有外部/内部中断事件发生,
    此时需要暂停当前正在执行的程序,并进行备份,转而去处理中断事件,
    处理完成后,返回当时程序执行位置继续执行。

  2. 举例
    老师正在讲课 (正常执行的程序)
    老徐叫我去开会 (外部中断信号)
    暂停讲课保存课堂笔记 (压栈保护现场)
    跳转到中断处理程序 (根据异常向量表进行跳转)
    开会 (执行中断处理程序)
    会后回来继续上课 (出栈恢复现场)

  3. 意义:提高突发事件的处理效率

  4. NVIC 嵌套向量中断控制器

    1. 管理中断
      ​ 每一个外部中断进来都可能被执行或禁止,并可以设置为挂起状态或清除状态。
      ​ 挂起:当处理器正在处理高优先级事件时,低优先级中断事件会先被设置为挂起状态。
      ​ 清除:当处理完中断处理程序时,可将该中断事件设置为清除状态。

    2. 支持中断或异常的向量化处理
      ​ 当异常或中断发生时,给定PC一个特定的地址,对应各个中断或异常处理程序的执行地址,这个按照优先级排列组成的地址列表称为中断向量表。
      异常:来自于处理器内部的异常事件
      ​ 中断:来自于处理器外部的中断事件

    3. 支持中断嵌套

      1. 有3个固定优先级,都是负值,不可改变。
      2. 优先级数越小,优先级越高。
      3. 由两个bit位控制的可配置优先级 00 01 10 11
      4. 抢占式优先级
        ​ 抢占,高优先级中断事件可以打断正在处理的低优先级中断事件
        ​ 响应式优先级
        ​ 两个中断事件同时到达时,处理响应优先级高的事件。

【实验】按键中断

​ 按键按下,触发中断控制串口打印“interrupt”

  1. 查看芯片原理图
    ​ 按键 – PA8
    ​ 使能UART1为异步通信模式 – PA9 PA10

  2. 在CubeMX中进行配置

    1. uart1配置为ASY异步模式
    2. PA8管脚配置为GPIO_EXTI8外部中断模式
    3. 在NVIC详细配置里使能外部中断EXTI4to15
  3. 打开工程
    ​ 启动文件中找到对应的中断向量
    DCD EXTI0_1_IRQHandler ; EXTI Line 0 and 1
    DCD EXTI2_3_IRQHandler ; EXTI Line 2 and 3
    DCD EXTI4_15_IRQHandler ; EXTI Line 4 to 15

  4. 找到中断处理函数

  void EXTI4_15_IRQHandler(void)
	  { 
        
	    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
	  }
  1. 继续跳转
    外部中断处理程序
 void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
	  { 
        
	    if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
	    { 
         
	      __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); //清楚中断标志
	      HAL_GPIO_EXTI_Callback(GPIO_Pin);   //处理中断回调函数
	    }
	  }
  1. 进入中断回调函数
  __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) //__weak弱化符 可重写该函数
  { 
              
    UNUSED(GPIO_Pin);
  }
  在gpio.c里重写中断回调函数:
  void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  { 
        
     if(GPIO_Pin == GPIO_PIN_8)  //判断是否为EXTI8中断事件
     { 
        
  	    printf("interrupt!");  //打印“interrupt”
  	 }
  
  }

【15】串口中断

  1. 串口发送完成中断 串口发送“hello”完成之后触发中断事件,在中断中发送“world”。

    1. 在CubeMX中将UART1设置为异步模式
    2. 在NVIC中使能串口全局中断
      USART1 global interrupt I USART1 wake-up interrupt through EXTI line 25
    3. 生成工程后编译,在启动文件中找到对应中断向量:
      DCD USART1_IRQHandler ; USART1
    void USART1_IRQHandler(void)
    {
      HAL_UART_IRQHandler(&huart1);
    }
    
    1. 找到串口中断处理函数
      void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
      发送完成中断
      UART_EndTransmit_IT(huart);
    2. 发送完成中断回调函数
      static void UART_EndTransmit_IT(UART_HandleTypeDef *huart)
      {
      ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_TCIE);
      huart->gState = HAL_UART_STATE_READY;
      huart->TxISR = NULL;
      #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
      huart->TxCpltCallback(huart);
      #else
      HAL_UART_TxCpltCallback(huart);
      #endif
      }
    3. 重新发送完成中断回调函数
      __weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
      {
      UNUSED(huart);
      }
      在uart.c中重写
      void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
      {
      HAL_UART_Transmit(&huart1,"world",sizeof("world"),100);
      }
    4. 在main.c中触发发送完成中断
      HAL_UART_Transmit_IT(&huart1,"hello",sizeof("hello"));
  2. 串口接收完成中断
    //重写接收完成中断回调函数

 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  { 
        
      HAL_UART_Transmit(&huart1,"Recive 4 char",sizeof("Recive 4 char"),100);
  }
  //main.c中触发接收中断
  while (1)
  { 
        
     HAL_UART_Receive_IT(&huart1,str,4);  //接收4个字符 触发一次中断 
     HAL_Delay(1000);
     printf("%s",str);
     memset(str,0,sizeof(str));
  }

【实验】五向按键电压采集实验

  1. 查看电路原理图
    找到按键ADC监测管脚 – PA0
  2. 配置CubeMX
    配置PA0管脚为ADC_CH0
    使能串口为异步通信模式
    使能RCC时钟配置时钟选择高速外部时钟48Mhz
  3. 生成工程
    需要使用的函数:
    HAL_ADC_Start 开始转换
    HAL_ADC_PollForConversion 等待转换
    HAL_ADC_GetValue 获取转换结果
    HAL_ADC_Stop 停止转换
  4. 代码实现:
 while(1)
	 { 
        
	    if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8))
	    { 
        
	    	 HAL_Delay(100);
	    	 if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_8))
	    	 { 
        
	            HAL_ADC_Start(&hadc);
	            HAL_ADC_PollForConversion(&hadc,100);
	            value = HAL_ADC_GetValue(&hadc);
	            HAL_ADC_Stop(&hadc);
	            printf("key:%d",value);
	         }
    }	 

【16】STM32的时钟系统

  1. 时钟系统是由振荡器(信号源),定时唤醒器,分频器等组成的电路。
    常用的信号源:石英晶体 RC振荡器
  2. 震荡不稳 停振 不起振
  3. 倍频:CPU需要更高的频率,石英晶体成本高,震荡频率高不稳定,所以需要把低频率倍频到高频率
    分频: CPU频率很高但是外设不需要这么高的频率
  4. STM32四个时钟源
    1. HSI 高速内部时钟 RC振荡器 8MHZ
    2. HSE 高速外部时钟 石英晶体/陶瓷 8MHZ
    3. LSI 低速内部时钟 RC振荡器 40KHZ 看门狗用的时钟源只能是这个,有时候也给RTC时钟源
    4. LSE 低速外部时钟 32.768KHZ的使用晶体 给RTC做时钟源
  • PLL(倍频器) 图10-时钟树

【17】systick定时器

  1. 定时器: 能够计数和定时的器件称为定时器,systick又叫滴答定时器,是一个定时设备,(定时的本质是计数-保证时钟信号是周期性的),systick位于Contex_M0的内核当中,和NVIC是捆绑的,产生一个systick的异常,IRQ异常号是15
  2. 滴答定时器 是一个24位的递减定时器,最多能计数2^24(0xFFFFFF),减到0的时候出发中断,再次重装值继续减1

【实验】 追Systick - 8M的时基

  1. 配置CubeMx 时钟树-高速内部时钟-8M(使用默认)
  2. DCD SysTick_Handler
 void SysTick_Handler(void)
   { 
        
    HAL_IncTick();
   }
   __weak void HAL_IncTick(void)
   { 
        
    uwTick += uwTickFreq;
    //uwTick +=1 ; --> uwTick++;
   }
   
   __IO uint32_t uwTick;  // __IO防止编译器优化 uwTick是个全局变量 初始值是0
   HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT; //枚举类型
   HAL_TICK_FREQ_1KHZ = 1U //就是1
   HAL_TICK_FREQ_DEFAULT = HAL_TICK_FREQ_1KHZ
  1. main.c
    /* Reset of all peripherals, Initializes the Flash interface and the Systick. /
    初始化外设 flash和Systick
    HAL_Init();
    /
    Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */
    使用高速内部时钟配置systick时基是1ms
    (就算配置了48M在复位之后第一次也是HSI-8M)
    HAL_InitTick(TICK_INT_PRIORITY);
    Care must be taken:
    外设中断调用HAL_Dela的时候,要注意中断优先级的问题,必须要提高systick的优先级(数字越小越高),否则会遇到阻塞/屏蔽的情况
  • 1.中断处理

相关文章