当前位置:   article > 正文

STM32

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

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/464115
推荐阅读
相关标签
  

闽ICP备14008679号