• STM32


    一.看门狗

     1.独立看门狗

      1.1介绍

    	在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造
    成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会
    造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测
    的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”
    (watchdog) 。独立看门狗工作在主程序之外,能够完全独立工作,它的时钟是专用的低速时钟(LSI),由
    VDD 电压供电, 在停止模式和待机模式下仍能工作。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    	本质是一个 12 位的递减计数器,当计数器的值从某个值一直减到0的时候,系统就会产生一个复
    位信号,即 IWDG_RESET 。
    如果在计数没减到0之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们
    经常说的喂狗。
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    	独立看门狗的时钟由独立的RC振荡器LSI提供,即使主时钟发生故障它仍然有效,非常独立。启用
    IWDG后,LSI时钟会自动开启。LSI时钟频率并不精确,F1用40kHz。
    	LSI经过一个8位的预分频器得到计数器时钟
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    	重装载寄存器是一个12位的寄存器,用于存放重装载值,低12位有效,即最大值为4096,这个值
    的大小决定着独立看门狗的溢出时间
    
    • 1
    • 2

    在这里插入图片描述

    	键寄存器IWDG_KR可以说是独立看门狗的一个控制寄存器,主要有三种控制方式,往这个寄存器
    写入下面三个不同的值有不同的效果。
    
    • 1
    • 2

    在这里插入图片描述

      1.2实验

    	开启独立看门狗,溢出时间为1秒,使用按键1进行喂狗。
    	溢出时间计算:
    	PSC=64,RLR=625
    
    • 1
    • 2
    • 3
    #include "string.h"
    int main(void)
    {
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_IWDG_Init();
      MX_USART1_UART_Init();
      HAL_UART_Transmit(&huart1,(const unsigned char*)"程序启动...\n",strlen("程序启动...\n"),100);
      while (1)
      {
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
            HAL_IWDG_Refresh(&hiwdg);
            HAL_Delay(50);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

     2.窗口看门狗

      2.1介绍

    	窗口看门狗用于监测单片机程序运行时效是否精准,主要检测软件异常,一般用于需要精准检测
    程序运行时间的场合。
    	窗口看门狗的本质是一个能产生系统复位信号和提前唤醒中断的6位计数器
    
    • 1
    • 2
    • 3
    产生复位条件:
    	当递减计数器值从 0x40 减到 0x3F 时复位(即T6位跳变到0)
    	计数器的值大于 W[6:0] 值时喂狗会复位。
    产生中断条件:
    	当递减计数器等于 0x40 时可产生提前唤醒中断 (EWI)。
    
    
    在窗口期内重装载计数器的值,防止复位,也就是所谓的喂狗。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    	Tout是WWDG超时时间(没喂狗)
    	Fwwdg是WWDG的时钟源频率(最大36M)
    	4096是WWDG固定的预分频系数
    	2^WDGTB是WWDG_CFR寄存器设置的预分频系数值
    	T[5:0]是WWDG计数器低6位,最多63
    
    • 1
    • 2
    • 3
    • 4
    • 5

      2.2实验

    	开启窗口看门狗,计数器值设置为 0X7F ,窗口值设置为 0X5F ,预分频系数为 8 。程序启动时点
    亮 LED1 ,300ms 后熄灭。在提前唤醒中断服务函数进行喂狗,同时翻转 LED2 状态。
    
    • 1
    • 2

    在这里插入图片描述

    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
    {
        HAL_WWDG_Refresh(hwwdg);
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
    }
    /* USER CODE END 0 */
    
    /**
      * @brief  The application entry point.
      * @retval int
      */
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
      HAL_Delay(300);
      MX_WWDG_Init();
      /* USER CODE BEGIN 2 */
    
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
          HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
          HAL_Delay(40);;
          
      }
      /* USER CODE END 3 */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

     3.总结

    在这里插入图片描述

    二.DMA

     1.介绍

    数据搬运工-代替cpu搬运数据
    代替 CPU 搬运数据,为 CPU 减负。
    1. 数据搬运的工作比较耗时间;
    2. 数据搬运工作时效要求高(有数据来就要搬走);
    3. 没啥技术含量(CPU 节约出来的时间可以处理更重要的事)。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    搬运存储器、外设数据
    	这里的外设指的是spi、usart、iic、adc 等基于APB1 、APB2或AHB时钟的外设,
    	而这里的存储器包括自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问地源或者目的
    
    • 1
    • 2
    • 3
    三种搬运方式:
    存储器→存储器(例如:复制某特别大的数据buf)
    存储器→外设 (例如:将某数据buf写入串口TDR寄存器)
    外设→存储器 (例如:将串口RDR寄存器写入某数据buf)
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    	STM32F103有2个 DMA 控制器,
    	DMA1有7个通道,DMA2有5个通道。
    	一个通道每次只能搬运一个外设的数据!! 如果同时有多个外设的 DMA 请求,则按照优先级进行响应。
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    在这里插入图片描述

    DMA及通道优先级
    优先级管理采用软件+硬件:
    	软件: 每个通道的优先级可以在DMA_CCRx寄存器中设置,有4个等级最高级>高级>中级>低级
    	硬件: 如果2个请求,它们的软件优先级相同,则较低编号的通道比较高编号的通道有较高的优先权。
    	比如:如果软件优先级相同,通道2优先于通道4
    
    DMA传输方式
    	DMA_Mode_Normal(正常模式)
    		一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次
    	DMA_Mode_Circular(循环传输模式)
    		当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式
    	指针递增模式
    		外设和存储器指针在每次传输后可以自动向后递增或保持常量。
    		当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

     2.实验

    HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
    
    参数一:DMA_HandleTypeDef *hdma,DMA通道句柄
    参数二:uint32_t SrcAddress,源内存地址
    参数三:uint32_t DstAddress,目标内存地址
    参数四:uint32_t DataLength,传输数据长度。注意:需要乘以sizeof(uint32_t)
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    
    __HAL_DMA_GET_FLAG
    #define __HAL_DMA_GET_FLAG(__HANDLE__, __FLAG__) (DMA1->ISR & (__FLAG__))
    参数一:HANDLE,DMA通道句柄
    参数二:FLAG,数据传输标志。DMA_FLAG_TCx表示数据传输完成标志
    返回值:FLAG的值(SET/RESET)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      2.1内存搬运到内存

    
    	实验:
    		使用DMA的方式将数组A的内容复制到数组B中,搬运完之后将数组B的内容打印到屏幕。
    	代码:
    		1. 开启数据传输
    		2. 等待数据传输完成
    		3. 打印数组内容
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    #define BUF_SIZE 16
    // 源数组
    uint32_t srcBuf[BUF_SIZE] = {
    	0x00000000,0x11111111,0x22222222,0x33333333,
    	0x44444444,0x55555555,0x66666666,0x77777777,
    	0x88888888,0x99999999,0xAAAAAAAA,0xBBBBBBBB,
    	0xCCCCCCCC,0xDDDDDDDD,0xEEEEEEEE,0xFFFFFFFF
    };
    // 目标数组
    uint32_t desBuf[BUF_SIZE];
    int fputc(int ch, FILE *f)
    {
    	unsigned char temp[1]={ch};
    	HAL_UART_Transmit(&huart1,temp,1,0xffff);
    	return ch;
    }
    main函数里:
    // 开启数据传输
    HAL_DMA_Start(&hdma_memtomem_dma1_channel1,
    (uint32_t)srcBuf, (uint32_t)desBuf, sizeof(uint32_t) * BUF_SIZE);
    // 等待数据传输完成
    while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1, DMA_FLAG_TC1) == RESET);
    // 打印数组内容
    for (i = 0; i < BUF_SIZE; i++)
    	printf("Buf[%d] = %X\r\n", i, desBuf[i]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

      2.1内存搬运到外设

    	使用DMA的方式将内存数据搬运到串口1发送寄存器,同时闪烁LED1。
    
    • 1
    HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData,uint16_t Size)
    
    参数一:UART_HandleTypeDef *huart,串口句柄
    参数二:uint8_t *pData,待发送数据首地址
    参数三:uint16_t Size,待发送数据长度
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    int main(void)
    {
      /* USER CODE BEGIN 1 */
        int i = 0;
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      for(i=0;i<BUF_SIZE;i++){
        sendBuf[i] = 'A';
      }
      //将数据发送到串口
      HAL_UART_Transmit_DMA(&huart1,sendBuf,BUF_SIZE);
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
        
        /* USER CODE BEGIN 3 */
          HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
          HAL_Delay(100);
      }
      /* USER CODE END 3 */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

      2.1外设搬运到内存

    使用DMA的方式将串口接收缓存寄存器的值搬运到内存中,同时闪烁LED1。
    
    • 1
    #define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__) ((((__INTERRUPT__) >> 28U)
    == UART_CR1_REG_INDEX)? ((__HANDLE__)->Instance->CR1 |= ((__INTERRUPT__) &
    UART_IT_MASK)): \
    (((__INTERRUPT__) >> 28U)
    == UART_CR2_REG_INDEX)? ((__HANDLE__)->Instance->CR2 |= ((__INTERRUPT__) &
    UART_IT_MASK)): \
    ((__HANDLE__)->Instance-
    >CR3 |= ((__INTERRUPT__) & UART_IT_MASK)))
    
    参数一:HANDLE,串口句柄
    参数二:INTERRUPT,需要使能的中断
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData,uint16_t Size)
    参数一:UART_HandleTypeDef *huart,串口句柄
    参数二:uint8_t *pData,接收缓存首地址
    参数三:uint16_t Size,接收缓存长度
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    #define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR &
    (__FLAG__)) == (__FLAG__))
    
    参数一:HANDLE,串口句柄
    参数二:FLAG,需要查看的FLAG
    返回值:FLAG的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    #define __HAL_UART_CLEAR_IDLEFLAG(__HANDLE__) __HAL_UART_CLEAR_PEFLAG(__HANDLE__)
    参数一:HANDLE,串口句柄
    返回值:无
    
    • 1
    • 2
    • 3
    HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
    参数一:UART_HandleTypeDef *huart,串口句柄
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    
    • 1
    • 2
    • 3
    #define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)
    参数一:HANDLE,串口句柄
    返回值:未传输数据大小
    
    • 1
    • 2
    • 3
    
    如何判断串口接收是否完成?如何知道串口收到数据的长度?
    	使用串口空闲中断(IDLE)!
    	串口空闲时,触发空闲中断;
    	空闲中断标志位由硬件置1,软件清零
    	利用串口空闲中断,可以用如下流程实现DMA控制的任意长数据接收:
    	1. 使能IDLE空闲中断;
    	2. 使能DMA接收中断;
    	3. 收到串口接收中断,DMA不断传输数据到缓冲区;
    	4. 一帧数据接收完毕,串口暂时空闲,触发串口空闲中断;
    	5. 在中断服务函数中,清除中断标志位,关闭DMA传输(防止干扰);
    	6. 计算刚才收到了多少个字节的数据。
    	7. 处理缓冲区数据,开启DMA传输,开始下一帧接收
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    //main.c
    uint8_t rcvBuff[BUF_SIZE];  //接收数据缓存数组
    uint8_t rcvLen = 0; //接收一帧数据的长度
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);   //使能空闲中断
      HAL_UART_Receive_DMA(&huart1,rcvBuff,rcvLen); //使能DMA接收中断
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
          HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
          HAL_Delay(100);
      }
      /* USER CODE END 3 */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    //stm32f1xx_it.c
    void USART1_IRQHandler(void)
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
    
      /* USER CODE END USART1_IRQn 0 */
      HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
      if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET){   //判断IDLE标志位
        __HAL_UART_CLEAR_IDLEFLAG(&huart1); //清除标志位
        HAL_UART_DMAStop(&huart1);   //停止dma传输防止干扰
        uint8_t temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);  //获取没有发送完的数据
        rcvLen = BUF_SIZE - temp;   //计算已经发送的数据
        HAL_UART_Transmit_DMA(&huart1,rcvBuff,rcvLen);  //发送数据
        HAL_UART_Receive_DMA(&huart1,rcvBuff,BUF_SIZE);
      }
      /* USER CODE END USART1_IRQn 1 */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    三.ADC

     1.介绍

    	模拟/数字转换器
    
    • 1

    在这里插入图片描述

    ADC特性
    	12位精度下转换速度可高达1MHZ
    	供电电压:V SSA :0V,V DDA :2.4V~3.6V
    	ADC输入范围:VREF- ≤ VIN ≤ VREF+
    	采样时间可配置,采样时间越长, 转换结果相对越准确, 但是转换速度就越慢
    	ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    ADC通道
    	总共2个ADC(ADC1,ADC2),每个ADC有18个转换通道: 16个外部通道、 2个内部通道(温度传感器、内部参考电压)
    	外部的16个通道在转换时又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4路。
    	规则通道:正常排队的人
    	注入通道:有特权的人
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    ADC转换顺序
    	每个ADC只有一个数据寄存器,16个通道一起共用这个寄存器,所以需要指定规则转换通道的转
    换顺序。
    	规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄
    存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通
    道就是第x个转换
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    	和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器来控制,控制关系如下:
    
    • 1

    在这里插入图片描述

    	注入序列的转换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始。只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。
    
    • 1
    ADC触发方式
    	1. 通过向控制寄存器ADC-CR2的ADON位写1来开启转换,写0停止转换。
    	2. 也可以通过外部事件(如定时器)进行转换。
    
    • 1
    • 2
    • 3
    ADC转化时间
    	ADC是挂载在APB2总线(PCLK2)上的,经过分频器得到ADC时钟(ADCCLK),最高 14 MHz。
    	转换时间=采样时间+12.5个周期
    	12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us
    
    • 1
    • 2
    • 3
    • 4
    ADC转换模式
    扫描模式
    	关闭扫描模式:只转换ADC_SQRx或ADC_JSQR选中的第一个通道
    	打开扫描模式:扫描所有被ADC_SQRx或ADC_JSQR选中的所有通道
    单次转换/连续转换
    	单次转换:只转换一次
    	连续转换:转换一次之后,立马进行下一次转换
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

     2.实验

    使用ADC读取烟雾传感器的值
    
    • 1
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
          HAL_ADC_Start(&hadc1);    //启动ADC1
          HAL_ADC_PollForConversion(&hadc1,50); //等待ADC转换完成
          uint32_t somkeValue = HAL_ADC_GetValue(&hadc1);
          //printf("somkeValue = %d\r\n", somkeValue);
          printf("smoke_value = %f\r\n", 3.3/4096 * smoke_value);
          HAL_Delay(500);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    三.IIC

    HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c,
    					uint16_t DevAddress,
    					uint16_t MemAddress,
    					uint16_t MemAddSize,
    					uint8_t *pData,
    					uint16_t Size,
    					uint32_t Timeout)
    参数一:I2C_HandleTypeDef *hi2c,I2C设备句柄
    参数二:uint16_t DevAddress,目标器件的地址,七位地址必须左对齐
    参数三:uint16_t MemAddress,目标器件的目标寄存器地址
    参数四:uint16_t MemAddSize,目标器件内部寄存器地址数据长度
    参数五:uint8_t *pData,待写的数据首地址
    参数六:uint16_t Size,待写的数据长度
    参数七:uint32_t Timeout,超时时间
    返回值:HAL_StatusTypeDef,HAL状态(OK,busy,ERROR,TIMEOUT)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    向OLED写命令的封装
    void Oled_Write_Cmd(uint8_t dataCmd)
    {
    	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x00, I2C_MEMADD_SIZE_8BIT,&dataCmd, 1, 0xff);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    向OLED写数据的封装:
    void Oled_Write_Data(uint8_t dataData)
    {
    HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,&dataData, 1, 0xff);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    四.SPI

     1.介绍

    	SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,
    	并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如AT91RM9200 。
    
    • 1
    • 2

    在这里插入图片描述

    SPI 包含 4 条总线,SPI 总线包含 4 条总线,分别为SS、SCK、MOSI、MISO。它们的作用介绍如
    下 :
    	(1) MISO – Master Input Slave Output,主设备数据输入,从设备数据输出 
    	(2) MOSI – Master Output Slave Input,主设备数据输出,从设备数据输入 
    	(3) SCK – Serial Clock,时钟信号,由主设备产生 
    	(4) CS – Chip Select,片选信号,由主设备控制
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    工作原理
    
    • 1

    在这里插入图片描述

    SPI工作模式
    时钟极性(CPOL):
    	没有数据传输时时钟线的空闲状态电平 
    	0:SCK在空闲状态保持低电平 
    	1:SCK在空闲状态保持高电平
    时钟相位(CPHA):
    	时钟线在第几个时钟边沿采样数据 
    	0:SCK的第一(奇数)边沿进行数据位采样,数据在第一个时钟边沿被锁存 
    	1:SCK的第二(偶数)边沿进行数据位采样,数据在第二个时钟边沿被锁存
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    模式0和模式3时序图
    模式0:橙色的线处采样
    模式3:橙色的线处采样
    
    • 1
    • 2
    • 3

    在这里插入图片描述

     2.W25Q128

    	W25Q128 是华邦公司推出的一款 SPI 接口的 NOR Flash 芯片,其存储空间为 128 Mbit,相当于16M 字节。
    	Flash 是常用的用于储存数据的半导体器件,它具有容量大,可重复擦写、按“扇区/块”擦除、掉电后数据可继续保存的特性。
    	Flash 是有一个物理特性:只能写 0 ,不能写 1 ,写 1 靠擦除。(里面默认是1
    • 1
    • 2
    • 3

    在这里插入图片描述

    256个块,一个块16个扇区,一个扇区16页,每页256个字节
    一般按扇区(4k)进行擦除。
    可以按 章 ------ 字 进行理解。
    
    • 1
    • 2
    • 3
    W24Q128常用命令
    
    • 1

    在这里插入图片描述

    写使能 (06H)
    	执行页写,扇区擦除,块擦除,片擦除,写状态寄存器等指令前,需要写使能。
    	拉低CS片选 → 发送06H → 拉高CS片选
    
    读状态寄存器(05H)
    	拉低CS片选 → 发送05H→ 返回SR1的值 → 拉高CS片选
    
    读时序(03H)
    	拉低CS片选 → 发送03H→ 发送24位地址 → 读取数据(1~n) → 拉高CS片选
    
    页写时序 (02H)
    	页写命令最多可以向FLASH传输256个字节的数据。
    	拉低CS片选 → 发送02H→ 发送24位地址 → 发送数据(1~n) → 拉高CS片选
    
    
    扇区擦除时序(20H)
    	写入数据前,检查内存空间是否全部都是 0XFF ,不满足需擦除。
    	拉低CS片选 → 发送20H→ 发送24位地址 → 拉高CS片选
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    	等待空闲:等待写入完成
    	写操作
    
    • 1
    • 2

    在这里插入图片描述

    无校验的写
    
    • 1

    在这里插入图片描述

  • 相关阅读:
    思科设备命令那么多,这10个一定是最常用的。
    Avalonia for VSCode
    xpath
    Java学习笔记(三):抽象类
    抽象类和接口有什么区别?
    Unity-CharacterController(角色控制器)
    SpringBoot实用开发之热部署
    【LIN总线测试】——LIN主节点物理层测试
    AM@分部积分原理和应用
    el-table动态配置显示表头
  • 原文地址:https://blog.csdn.net/qq_46626969/article/details/137870874