CubeMX配置STM32外设流程

GPIO开发

掌握两个GPIO输出的HAL库函数

GPIO电平输出HAL库函数
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
理解三个参数:
/*1-GPIOx:目标引脚的端口号
 *2-GPIO_Pin:目标引脚的引脚号
 *3-PinState:高电平--GPIO_PIN_SET;低电平--高电平--GPIO_PIN_RESET
*/
例:向PB8引脚输出高电平
    HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
GPIO电平翻转HAL库函数
void HAL_GPIO_TogglePin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin)
例:将PA3的引脚输出电平翻转
     HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);

跑马灯实验:以STM32F103C8T6为例

CubeMx配置

1.通用配置以后所有模块第一步先配置调试接口System Core -->SYS–>Debug(Serial Wire)

2.设置时钟(更精确)(可选),不设置的话默认使用内部时钟RCC–>外部高速时钟(外部低速时钟)二选一–>Crystal/Ceramic Resonator

​ 内部时钟是由单片机内部的振荡器或晶体产生的,而外部时钟是由外部提供的稳定时钟信号源提供的

​ 由于外部时钟是由外部提供的稳定时钟信号源,因此它通常具有更高的稳定性和精度。 而内部时钟则受到单片机内部环境和温度的影响,稳定性和精度可能较低。

3.设置时钟树(见时钟树章节),在HCLK里输入72(具体看芯片最大时钟)即可

​ 以上均为通用模块配置

4.将引脚(我这里LED为PC13引脚)设置为输出模式,其他GPIO初始化(速率,模式等)在GPIO中配置 这里配置为开漏模式(GPIO八种工作模式)低电平点亮LED

5.最后在Project Manager配置(后面不再掩饰)

1

GPIIO

GPIO1

第三个为输入模式在按键开发中设置为输入上拉模式,默认为浮空

a119b773070ac2e768387ca4ef19c9b

16a54f9364956154b4993cbbe6d365e

实验代码

#include "main.h"
#include "gpio.h"

void SystemClock_Config(void);

int main(void)
{
  //初始化
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  while (1)
  {
      //逻辑代码
	  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);// HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
	  HAL_Delay(500);
	  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_3);
	  HAL_Delay(500);
  }
}
//以下代码都为CUBEMX初始化自动生成,以后将直接忽视
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{

}
#endif 

按键开发

HAL库中关于GPIO的3个重要函数

电平输出函数

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

电平翻转函数

void HAL_GPIO_TogglePin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin)

电平输入函数

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

例:判断PC13引脚的输入信号,若为高电平则将PB9引脚控制的LED灯的开关状态切换

if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13) == GPIO_PIN_SET)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
}

实例:按下KEY2,切换LED1的开关状态

​ 按下KEY3,松开后切换LED2的开关状态

​ 按下KEY3,关闭所有LED

实例代码

#include "main.h"
#include "gpio.h"
#define KEY2 HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_5)
#define KEY3 HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)
#define KEY4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
void SystemClock_Config(void);
void Scan_Keys(void);
void Scan_Keys()
{
	if(KEY2 == 0)
	{
		HAL_Delay(10);
		if(KEY2 == 0)
		{
			HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_0);
			while(KEY2 == 0);//等待按键松开 防止代码重复执行
		}
	}
	if(KEY3 == 0)
	{
		HAL_Delay(10);
		{
			if(KEY3 == 0)
			{
				while(KEY3 == 0);//等待按键松开
				HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_1);
			}
		}
	}
	if(KEY4 == 0)
	{
		HAL_Delay(10);
		{
			if(KEY4 == 0)
			{
				HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0|GPIO_PIN_1,1);
                while(KEY == 0);
			}
		}
	}
}
int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  while (1)
  {
	  Scan_Keys();
  }
}

外部中断

EXTI0,EXTI1,EXTI2,EXTI3,EXTI4专用(EXTI0的引脚为每个端口 的0号引脚PA0 PB0~PG0)

EXTI5~~EXTI9共用

EXTI10~~EXTI15共用

外部中断程序设计思路

传统标准库中断设计步骤:

1.将GPIO初始化为输入端口

2.配置相关I/O引脚与中断的映射关系

3.设置I/O引脚对应的中断触发条件

4.配置NVIC

5.设置中断优先级

6.编写中断服务函数

基于CubeMX的外部中断设计步骤

1.在CubeMX中指定引脚,配置中断初始化参数

2.重写I/O引脚对应的中断回调函数

例:将PC13引脚设置为外部中断,下降沿触发,在中断服务函数中翻转PB9引脚的电平信号

中断初始化配置

1.将GPIO设置为:GPIO_EXTI功能

2.设置中断触发条件:上升沿,下降沿,上升沿或下降沿

3.使能相关的NVIC通道

实训:将key2(PC13)设置为外部中断输入,下降沿触发在中断服务函数中切换LED1的开关状态

​ 将key4(PB5)设置为外部中断输入,上升沿触发在中断服务函数中切换LED2的开关状态

CubeMX配置,将PB8,PB9设置为开漏输出,将PB5,PC13设置为GPIO_EXTIx,

GPIO mode 将PB5设置为上升沿,PC13设置为下降沿都输入上拉

然后使能NVIC

外部中断

#include "main.h"
#include "gpio.h"

void SystemClock_Config(void);
//HAL库中所有中断服务函都跳转到同一个回调函数,只用判断产生中断的引脚作出对应的处理即可
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == GPIO_PIN_13)
	{
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
	}
	if(GPIO_Pin == GPIO_PIN_13)
	{
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
	}
}
int main(void)
{

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();

  while (1)
  {

  }
}

定时器

CubeMX关于定时器的配置

1.设置Clock Source时钟源

2.设置Prescaler和Couter Period参数

3.设置NVIC嵌套向量中断控制器

CubeMX配置定时器

定时器1选择定时器 2选择时钟源(一般用内部时钟) 3参数设置 4使能中断

55718bd67f219281d5910707f79965e

PSC一般设置为7,Counter Mode选择为上计数 auto-reload(使能预加载更安全)

参数设置原理

e70a7ed80825d2465d28f9251e05a96

72749b11df2a45375efb7526c0595bd

09ad2068a21f21d4afbdf2b9aac6e57

de8e6fa9535aa0e2f2e22efd3d0e956

自制延时函数(时基单元)

#include "main.h"
#include "tim.h"
#include "gpio.h"
void SystemClock_Config(void);
static void MyDelay(uint32_t Delay);
static uint32_t MyGetTick(void);//获取单片机当前时间,返回单片机当前的时间(ms)
static volatile uint32_t currentMiliSeconds = 0;

static void MyDelay(uint32_t Delay)
{
	uint32_t endtime = MyGetTick() + Delay;//延迟结束时间=当前时间+要延迟的时间
	while(MyGetTick() < endtime){}//等待延迟结束
}
static uint32_t MyGetTick()
{
	return currentMiliSeconds;
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim1)
	{
		currentMiliSeconds++;//时间自增每秒进一次中断时间+1
	}
}
int main(void)
{

  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();
  
  HAL_TIM_Base_Start_IT(&htim1);//以中断方式启动定时器
	
  while (1)
  {
	  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	  MyDelay(1000);
	  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
	  MyDelay(1000);  
  }
}

外部中断信号控制LED开关

1.利用TIM2实现间隔定时,每隔0.2s将LED1的开关状态翻转(PSC=7,ARR=499,1MHZ/500=0.2s)

2.利用TIM3实现间隔定时,每隔1s将LED2的开关状态翻转(PSC=7,ARR=999,1MHZ/1000=1S)

#include "main.h"
#include "tim.h"
#include "gpio.h"

void SystemClock_Config(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim2)
	{
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
	}
	if(htim == &htim3)
	{
		HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_9);
	}
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
 
  HAL_TIM_Base_Stop_IT(&htim2);
  HAL_TIM_Base_Stop_IT(&htim3);
  while (1)
  {
	  
  }
}

PWM呼吸灯(输出比较)

62dcf08a36199133b0f32c529ea87e3

时基单元决定PWM的周期,周期=ARR+1,CCR决定高电压所占时间,占空比与CCR和ARR寄存器的值有关

channel1 PWMGeneration CH1 正常输出

84a3c52e670a566ac556c23aa6ead94

96d9aa2f54b942828ad8915e76a3a05

占空比 = 0.5sin(2πt)+0.5 t为单片机当前时间

#include "main.h"
#include "tim.h"
#include "gpio.h"
#include "math.h"

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();

  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//PWM正常输出
  while (1)
  {
	  float t = HAL_GetTick() * 0.001;//获取当前时间
	  float duty = 0.5 * sin(2*3.14*t) + 0.5;//计算占空比
	  uint16_t arr = __HAL_TIM_GET_AUTORELOAD(&htim1);//获取ARR寄存器的值
	  uint16_t ccr = duty * (arr+1);//计算CCR寄存器的值
	  __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,ccr);//将计算结果写入CCR
  }
}

超声波测距(输入捕获)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
工作原理:
在这里插入图片描述
测距需要捕获上升沿和下降沿 要用两个通道,外部信号检测只需要一个引脚,因此只能采用直接+间接的方式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Polarity Selection:上升沿或下降沿
IC Selection:信号来源(直接,间接,TRC)
PDR:选择分频系数:1,2,4,8
IF:输入滤波器参数

注:定时器定时周期应该大于测量信号最大脉宽,echo引脚最大脉宽为38ms,所以定时器周期应大于38ms
在这里插入图片描述
本次实验定时器配置图:
在这里插入图片描述
编程思路:
在这里插入图片描述
在这里插入图片描述
实验代码:

#include "main.h"
#include "tim.h"
#include "gpio.h"
void SystemClock_Config(void);
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM1_Init();
  while (1)
  {
	  //1让计数器清0
	  __HAL_TIM_SET_COUNTER(&htim1,0);
	  //2清除CC1,CC2标志位
	  __HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_CC1);
	  __HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_CC2);
	  //3启动输入捕获
	  HAL_TIM_IC_Start(&htim1,TIM_CHANNEL_1);
	  HAL_TIM_IC_Start(&htim1,TIM_CHANNEL_2);
	  //4向Trigger发送脉冲
	  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_1,GPIO_PIN_SET);
	  for(uint32_t i=0;i<10;i++);//循环一次消耗8个指令周期*1/8MHZ = 1us
	  HAL_GPIO_WritePin(GPIOE,GPIO_PIN_1,GPIO_PIN_RESET);
	  //5等待测量结束
	  uint8_t success = 0;
	  uint32_t expireTime = HAL_GetTick() + 50;//计算最长等待时间 整个测量过程最长不会超过50ms
	  
	  while(expireTime > HAL_GetTick())
	  {
		  uint32_t cc1Flag = __HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_CC1);//CC1标志位为从0-1捕获到上升沿
		  uint32_t cc2Flag = __HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_CC2);//CC2标志位从0-1捕获到下降沿
		  if(cc1Flag == cc2Flag ==1)
		  {
			success = 1;//测量成功
			break;
		  }
	  }
	  //6关闭定时器
	  HAL_TIM_IC_Stop(&htim1,TIM_CHANNEL_1);
	  HAL_TIM_IC_Stop(&htim1,TIM_CHANNEL_2);
	  //7计算测量结果
	  if(success ==1)
	  {
		uint16_t ccr1 = __HAL_TIM_GET_COMPARE(&htim1,TIM_CHANNEL_1);//读取ccr1 ccr2的值
		uint16_t ccr2 = __HAL_TIM_GET_COMPARE(&htim1,TIM_CHANNEL_2);
		float pulseWidth = (ccr2 - ccr1) * 1e-6f;//脉宽 = (CCR2 - CCR1) * 10的负6次方
		float distance = 340.0f * pulseWidth/2.0f;
		if(distance < 0.2)
		{
			HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
		}
		else
		{
			HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
		}
	  }
  }
}

占空比测量(从模式控制器)

定时器3通道一产生PWM,定时器1通道一测量PWM参数
定时器3配置:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Pulse为CCR寄存器值,设置占空比为20%,CH:High为正极性输出
定时器1配置:
在这里插入图片描述
需要从模式控制器来同时测量脉宽和周期
在这里插入图片描述
在这里插入图片描述
原理:
在这里插入图片描述
通道一捕获的是上升沿,每当PWM有上升沿时TI1FP1会产生一个脉冲,
TI1FP1作为TRGI每产生一个脉冲会将CNT清0来产生一个Update事件
当遇到下降沿时CNT的值会保存到CCR2中,遇到上升沿CNT的值会保存到CCR1中
所以CCR2的值等于脉宽,CCR1的值为PWM周期
编程思路
在这里插入图片描述
实验代码:

#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
void SystemClock_Config(void);
static void UART_Printf(const char *format,...);
#include<stdarg.h>
#include<stdio.h>
#include<string.h>
static void UART_Printf(const char *format,...)
{
	char tmp[128];
	va_list argptr;
	va_start(argptr,format);
	vsprintf((char* )tmp,format,argptr);
	va_end(argptr);
	
	HAL_UART_Transmit(&huart1,(const uint8_t *)&tmp,strlen(tmp),HAL_MAX_DELAY);
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  MX_TIM1_Init();
  //启动TIM3_CH1来产生PWM
  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
  while (1)
  {
	  //1清除CC1标志位
	  __HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_CC1);
	  //2启动 定时器
	  HAL_TIM_IC_Start(&htim1,TIM_CHANNEL_1);
	  HAL_TIM_IC_Start(&htim1,TIM_CHANNEL_2);
	  //3等待CC1标志位,并再次清除
	  while(__HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_CC1) == 0);
	  __HAL_TIM_CLEAR_FLAG(&htim1,TIM_FLAG_CC1);
	  //4再次等待cc1标志位
	  while(__HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_CC1) == 0);
	  //5关闭定时器
	  HAL_TIM_IC_Stop(&htim1,TIM_CHANNEL_1);
	  HAL_TIM_IC_Stop(&htim1,TIM_CHANNEL_2);
	  //6计算测量结果
	  uint16_t ccr1 = __HAL_TIM_GET_COMPARE(&htim1,TIM_CHANNEL_1);
	  uint16_t ccr2 = __HAL_TIM_GET_COMPARE(&htim1,TIM_CHANNEL_2);
	  float period = ccr1 * 1e-6;
	  float pulseWidth = ccr2 * 1e-6;
	  float duty = pulseWidth/period;
	  UART_Printf("脉宽 = %.1f,周期 = %.1f,占空比 = %.1f",pulseWidth*1e6f,period*1e6f,duty*100);
	  HAL_Delay(1000);
  }
}

串口

HAL库中串口发送的重要函数

阻塞式发送函数(推荐使用)数据没有发完单片机不能做其他事情

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)

非阻塞式发送函数(数据较多)

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

发送完毕中断回调函数

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

发一半中断回调函数

void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)

例:使用非阻塞式的串口发送函数,将发送缓数组dat_Txd中前5个数据发送到USART1,在数据发送完成后,翻转PB9的输出电平

HAL_UART_Transmit_IT(&huart1,dat_Txd,5);
void HAL_UART_TxCpltCallback(&huart1)
{
    if(huart->Instance == USART1)
    {
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
    }
}

阻塞式发送

HAL_UART_Transmit(&huart1,dat_Txd,5,HAL_MAX_DELAY);
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);

HAL库中串口接收的重要函数

阻塞式接收函数(不推荐)

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

非阻塞式接收函数

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

接收完毕中断回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

接收一半中断回调函数

void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)

例:使用非阻塞式串口接收函数,接收USART1中的一个字节将其保存在dat_Rxd变量中,在数据发送完毕后,若该字节为0x5A,翻转PB8引脚电平

HAL_UART_Receive_IT(&huart1,&dat_Rxd,1;
HAL_UART_RxCpltCallback()
{
    if(huart->Instance == USART1)
    {
        if(dat_Rxd == 0x5A)
            HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
    }
}

CubeMX配置串口

不涉及到中断的配置

选择通信方式

​ Connectivity -->2USART1–>3Mode(常用异步模式Asynchronous)–>4参数设置

GPIO-->USART-->串口数据接收引脚可能会意外断开 接个上拉电阻

3.mode:异步通信

4.参数设置

2

3

简单串口数据发送

#include<string.h>//使用strlen函数计算字符串大小
	uint8_t byteNumber = 0x5a;
	uint8_t byteArray[] = {1,2,3,4,5};
	char ch = 'a';
	char *str = "Hello world";
	HAL_UART_Transmit(&huart1,&byteNumber,1,HAL_MAX_DELAY);
	
	HAL_UART_Transmit(&huart1,byteArray,5,HAL_MAX_DELAY);
	
	HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,HAL_MAX_DELAY);
	
	HAL_UART_Transmit(&huart1,(uint8_t *)str,strlen(str),HAL_MAX_DELAY);

简单数据接收

通过串口向单片机发送1点亮LED,发送0熄灭LED

  while (1)
  {
		uint8_t dataRc;
		HAL_UART_Receive(&huart1,&dataRc,1,HAL_MAX_DELAY);
		if(dataRc == '0')
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_SET);
		}
		else if(dataRc == '1')
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_8,GPIO_PIN_RESET);
		}
  }

串口中断数据接收

通过串口来控制LED闪灯的快慢,发1慢闪,2正常,3快闪

板载LED连接PC13开漏接法 串口1连接串口转TTL 其他配置与串口一样 多一个使能NVIC中断优先级默认;

1Connectivity -->2USART1–>3Mode(常用异步模式Asynchronous)–>4参数设置–>5使能中断–>中断优先级设置(只有一个中断的时候默认)

NVIC

static uint32_t time =1000;
static uint8_t datarec;
void SystemClock_Config(void);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		if(datarec == '1')
		{
			time = 1000;
		}
		else if(datarec == '2')
		{
			time = 500;
		}
		else if(datarec == '3')
		{
			time = 100;
		}
		HAL_UART_Receive_IT(&huart1,&datarec,1);//再次开启中断 开启下一次字节的接收
	}
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_IT(&huart1,&datarec,1);//开启中断接收
  while (1)
  {
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
		HAL_Delay(time);
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
		HAL_Delay(time);
  }
}

外部中断信号控制LED灯开关

1.开机后,向串口1发送“hello world!”

2.串口1收到字节指令“0xA1”,打开LED1,发送“LED1 Open”

3.串口1收到字节指令“0xA2”,关闭LED1,发送“LED1 Closed”

4.在串口发送过程中,打开LED2作为发送数据指示灯

#include "main.h"
#include "usart.h"
#include "gpio.h"
#define LED1_ON() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET)
#define LED1_OFF()  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
#define LED2_ON() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_RESET)
#define LED2_OFF()  HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,GPIO_PIN_SET);
void SystemClock_Config(void);

uint8_t Tx_str1[] = "hello world!\r\n";
uint8_t Tx_str2[] = "LED Open\r\n";
uint8_t Tx_str3[] = "LED Closed\r\n";
uint8_t Rx_Date = 0;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1)
	{
		if(Rx_Date == 0xA1)
		{
			LED1_ON();
			
   		    LED2_ON();
			HAL_UART_Transmit(&huart1,Tx_str1,sizeof(Tx_str2),HAL_MAX_DELAY);
			LED2_OFF();
			HAL_UART_Receive_IT(&huart1,&Rx_Date,1);//每发完一帧数据重新开启接收
		}
		if(Rx_Date == 0xA2)
		{
			LED1_OFF();
			
			LED2_ON();
			HAL_UART_Transmit(&huart1,Tx_str1,sizeof(Tx_str3),HAL_MAX_DELAY);
			LED2_OFF();
			HAL_UART_Receive_IT(&huart1,&Rx_Date,1);
		}
	}
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();

  LED1_ON();
  HAL_UART_Transmit(&huart1,Tx_str1,sizeof(Tx_str1),HAL_MAX_DELAY);
  LED1_OFF();
	
  HAL_UART_Receive_IT(&huart1,&Rx_Date,1);
  while (1)
  {
	
  }
}

IIC

选择通信方式

1Connectivity -->IIC–>3Mode(常用标准IIC模式)–>4参数设置(stm32作为主机只设置上面参数,作从机设置下面的参数)

单片机只支持标准模式和快速模式(400kbs(400000))快速模式下占空比可调(2:1)

iic

简单数据收发点亮OLED

uint8_t command[] = {0x00,0x8d,0x14,0xaf,0xa5};//屏幕命令流
//0x78为从机地址
HAL_I2C_Master_Transmit(&hi2c1,0x78,command,sizeof(command)/sizeof(command[0]),HAL_MAX_DELAY);

uint8_t dataRcvd;//数据接收缓冲区

HAL_I2C_Master_Receive(&hi2c1,0x78,&dataRcvd,1,HAL_MAX_DELAY);

if((dataRcvd & (0x01 << 6 )) == 0)//D6=0表示屏幕亮  点亮LED
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
}
else
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
}
while(1)
{

}

时钟树

AHB总线 72MHZ APB1总线 32MHZ APB2总线 72MHZ

高速树:HSI HSE 低速树:LSI LSE

SYSCLK 72MHZ

1 RCC–>HSE(Crystal)选择外部时钟源–>锁相环选择HSE作为输入 倍频系数X9–>锁相环作为系统时钟输入–>AHB分频系数1 APB1为2 APB2为1

所有节点为最高频率

时钟树

SPI

串行外设接口:适用于高速,双向数据传输场景如摄像头模块

MOSI:主机发送从机接收

MISP:主机接收从机发送

SCK:串行时钟线

NSS:从机选择(低电压有效)

SPI五个参数

1波特率: 每秒钟传输高低电压的数量

2比特位传输顺序: MSB–>LSB LSB–>MSB

3数据位长度(8bit/16bit)

4时钟极性:低(时钟空闲状态下是低电压) 高(时钟空闲状态下是高电压) [上升沿叫第一边沿 下降沿为第二边沿]

5时钟相位:第一边沿采集 第二边沿采集

45可构成4种时钟模式

SPI波特率选取原则:1选择允许的最大值(通讯速度越快) 2考虑设备所能承受的极限 3考虑电路板所能承受极限

外部Flash存取输入输出实验:

要求:用按钮切换LED状态,按一下切换一次LED状态,每次按按钮的时候将LED的亮灭状态保存在外部Flash芯片中,这样即使断电LED的状态也不会消失

LED开漏接法

按钮输入上拉,松开按钮的时候引脚被上拉电阻拉成高电平,按下按钮时引脚接地;(按钮按下时没有反应,松开时切换,需要捕捉松开的瞬间)

48a5da865ccabb7ef3e7794c1a7aeb0

PA567自动生成,PA4手动设置为输出模式初始电平高电压推完输出输出速度50MHZ作为NSS接口

1Connectivity -->2SPI1–>3Mode(FULL-Duplex Master 全双工)–>4参数设置 低波特率更稳定,高极性 第二边沿采集

SPI

static void SaveLEDState(uint8_t LEDState);//保存LED状态
static uint8_t LoadLEDState(void);//加载LED状态
static uint8_t LoadLEDState(void)
{
	uint8_t readDataCmd[] = {0x03,0x00,0x00,0x00};
	uint8_t LEDState = 0xff;//用来接收读取的数据
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);//拉低NSS
	HAL_SPI_Transmit(&hspi1,readDataCmd,4,HAL_MAX_DELAY);
	HAL_SPI_Receive(&hspi1,&LEDState,1,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//拉高NSS
	return LEDState;
}
static void SaveLEDState(uint8_t LEDState)
{
	//1写使能
	uint8_t writeEnableCmd[] = {0x06};
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,writeEnableCmd,1,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	//2扇区擦除
	uint8_t sectorErase[] = {0x20,0x00,0x00,0x00};
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,sectorErase,4,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	HAL_Delay(100);
	//3写使能
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,writeEnableCmd,1,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	//4页编程
	uint8_t pageProgCmd[5];
	pageProgCmd[0] = 0x02;
	pageProgCmd[1] = 0x00;
	pageProgCmd[2] = 0x00;
	pageProgCmd[3] = 0x00;
	pageProgCmd[4] = 0x00;
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
	HAL_SPI_Transmit(&hspi1,pageProgCmd,5,HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
	HAL_Delay(10);
}
    uint8_t pre = 1;		//程序开始前按钮是松开的(高电平)
	uint8_t current = 1;
	uint8_t LEDState = 0;//定义LED初始状态为熄灭
	
	LEDState = LoadLEDState();
	if(LEDState == 1)
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	}
	else
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
	}
  while (1)
  {
		pre = current;
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_SET)//判断引脚电平为高电压(按钮处于松开状态)
		{
			current = 1;
		}
		else//按钮按下
		{
			current = 0;
		}
		if(pre != current)//捕捉到了按钮按下的瞬间(消抖)需要进一步判断是按钮按下的瞬间还是松开的瞬间
		{
			HAL_Delay(10);
			if(current == 0){}//按钮按下
			else//按钮松开 需要切换LED状态
			{
				if(LEDState == 1)//LED是点亮的
				{
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//熄灭LED
					LEDState = 0;
				}
				else
				{
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);//点亮LED
					LEDState = 1;
				}
				SaveLEDState(LEDState);//切换状态保存
			}
		}
  }
}

    uint8_t pre = 1;		//程序开始前按钮是松开的(高电平)
	uint8_t current = 1;
	uint8_t LEDState = 0;//定义LED初始状态为熄灭
	
	LEDState = LoadLEDState();
	if(LEDState == 1)
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	}
	else
	{
		HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
	}
  while (1)
  {
		pre = current;
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_SET)//判断引脚电平为高电压(按钮处于松开状态)
		{
			current = 1;
		}
		else//按钮按下
		{
			current = 0;
		}
		if(pre != current)//捕捉到了按钮按下的瞬间(消抖)需要进一步判断是按钮按下的瞬间还是松开的瞬间
		{
			HAL_Delay(10);
			if(current == 0){}//按钮按下
			else//按钮松开 需要切换LED状态
			{
				if(LEDState == 1)//LED是点亮的
				{
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//熄灭LED
					LEDState = 0;
				}
				else
				{
					HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);//点亮LED
					LEDState = 1;
				}
				SaveLEDState(LEDState);//切换状态保存
			}
		}
  }
}
  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Flyik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值