stm32攻略(随时更新,更新时间:2023.5.5)

目录

1 CubeMX基本配置(针对stm32F407ZGT6,时钟树的配置需要根据单片机的种类而定)

2  接线

2.2  STM32F407ZG单片机连接USB转TTL(用于串口通信)

3  串口通信(附加中断+定时器+PWM综合)

3.1  printf/scanf的重定向

3.2  HAL_UART_Transmit()和HAL_UART_Receive()函数

3.3   中断及中断回调函数

3.3.1  中断函数

3.3.2  中断回调函数

3.4  DMA

 3.5  综合基础知识,通过串口通信控制

4  舵机

4.1  相关计算

4.2  接线

5  PID控制算法

5.1  简介

5.2  比例Proportional

5.3  积分Integral

5.4  微分Differential

5.5  公式

5.6  积分限幅

5.7  积分限行

5.8  相关代码

6  systick(定时器,多用于延时)

6.1  寄存器

 6.2  代码

 7  蓝牙模块


1 CubeMX基本配置(针对stm32F407ZGT6,时钟树的配置需要根据单片机的种类而定)

2  接线

单片机             ST_LINK

     9--------------------6

     7--------------------2

     20------------------3/4

     1--------------------7/8

 这里附上具体的接线图片:

2.2  STM32F407ZG单片机连接USB转TTL(用于串口通信)

单片机      USB转TTL

TX————RXD

RX————TXD

GND————GND

5V————5V

3  串口通信(附加中断+定时器+PWM综合)

3.1  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];
}

3.2  HAL_UART_Transmit()和HAL_UART_Receive()函数

HAL_UART_Transmit()是HAL库中的一个函数,用于向UART发送数据。该函数需要传入UART句柄、发送数据的缓冲区指针和发送数据的长度作为参数。在调用该函数时,UART会将缓冲区中的数据发送出去。如果发送成功,该函数会返回HAL_OK,否则会返回错误码。

参数1:使用的串口,2:要发送的数据,3:数据大小,4:发送的超时时间
    HAL_UART_Transmit(&huart1,Data,sizeof(Data),1000);

HAL_UART_Receive()函数同理,不再赘述

注意:这种发送方式是阻塞式(阻塞模式就像是一个延时函数,当这个函数没处理完那么,所有的按照流程需要执行的代码都不会被执行,要等到这个延时完成,类似 平时看书上写的LED灯闪烁,用的delay()一样。而非阻塞模式就像他定义的那样,一般用的是中断,执行这条语句的时候,开启相应的中断达到一定的条件才进行处理,这样不会影响到流程的执行.我的理解就是,阻塞就是死等,非阻塞就是中断)

3.3   中断及中断回调函数

3.3.1  中断函数

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

 HAL_UART_Receive_IT()函数同理

3.3.2  中断回调函数

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Transmit_IT(&huart1,buffer,100);
	HAL_UART_Receive_IT(&huart1,buffer,100);
}

3.4  DMA

DMA,全称Direct Memory Access,即直接存储器访问。

DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。

我们知道CPU有转移数据、计算、控制程序转移等很多功能,系统运作的核心就是CPU,

CPU无时不刻的在处理着大量的事务,但有些事情却没有那么重要,比方说数据的复制和存储数据,如果我们把这部分的CPU资源拿出来,让CPU去处理其他的复杂计算事务,是不是能够更好的利用CPU的资源呢?

因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理,
 

DMA就是基于以上设想设计的,它的作用就是解决大量数据转移过度消耗CPU资源的问题。有了DMA使CPU更专注于更加实用的操作–计算、控制等。

HAL_UART_Receive_DMA(&huart1,buffer,100);
	 __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);

注意:如果要想能够保证不止一次输入数据,需要在回调函数里写内容(放在it文件里)

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=100-__HAL_DMA_GET_COUNTER(huart1.hdmarx);
		//rintf("%d\r\n",len);*/
	}
	
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

 3.5  综合基础知识,通过串口通信控制

实现功能如下:

1.LED灯的亮灭

2.定时器中断控制LED灯按频率闪烁

3.PWM波控制呼吸灯

4.PWM波控制舵机转动

5.按键控制灯的亮灭

6.高电平控制蜂鸣器(注意蜂鸣器声音挺大的,第一次用的时候吓一跳)

注意事项:

1.头文件加一个stdio.h,因为用到了printf和scanf,并且需要重定向(这两个函数原本是在显示屏上进行的)

2.最好定义全局变量,防止值的改变

3.利用中断控制时在CubeMX配置时要注意打开global interrupt选项

4.我的单片机PF9和PF10引脚对应两个LED灯

5.PWM波在Keil编写程序时要加上Start函数打开PWM波

6.我加的printf(check)是用来检验程序是否正常运行

7.在使用PWM波控制呼吸灯时要看好灯对应的那个引脚连接了哪个定时器

8.在使用PWM波控制舵机时要看好自己开的定时器对应哪个引脚,插信号线要插对

9.有关舵机的具体计算在下面有,可以先看下面的再回来看

10.输入数据到buffer【0】的位置来控制程序

11.如果不加DMA_Stop()函数则后续输入的数据会累积到buffer【1】【2】等等,这样if判断句就没用了

12.windows换行的话需要同时写\r\n

13.按键和亮灯原理:(我的单片机KEY1对应PE3,KEY0对应PE4)

 14.按键达不到预期效果(PF9定时器占用了),意会即可

这样算是用到了所有stm32的基础知识,包括:

1.GPIO控制高低电平

2.定时器中断

3.PWM波控制

4.串口通信(阻塞、非阻塞、DMA)

CubeMX整体配置:

 

代码: 

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
uint8_t buffer[100];
uint8_t len;
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
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];
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UART_Transmit_DMA(&huart1,buffer,100);
	HAL_UART_Receive_DMA(&huart1,buffer,100);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10);
}
/* 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();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  MX_TIM14_Init();
  MX_TIM2_Init();
  MX_TIM3_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim14,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
	HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		//HAL_UART_Transmit(&huart1,"HELLO",5,0xFFFF);
		//uint8_t buffer[100];
		
		HAL_UART_Receive_DMA(&huart1,buffer,100);
	 __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
		//printf("check0\r\n");
		printf("%d\r\n",buffer[0]);
		HAL_Delay(1000);
		if(buffer[0]==1)
		{
			printf("check1\r\n");
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
			HAL_Delay(500);
		
			printf("check2\r\n");
			HAL_Delay(1000);
			//memset(buffer,0,sizeof(buffer));
		}
		if(buffer[0]==2)
		{
			printf("check3\r\n");
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
			HAL_Delay(500);
			printf("check4\r\n");
			HAL_Delay(1000);
			//memset(buffer,0,sizeof(buffer));
		}
		if(buffer[0]==3)
		{
			printf("check5\r\n");
			for(uint16_t cnt=0;cnt<1000;cnt++)
			{
				__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,cnt);
				HAL_Delay(1);
			}
			for(uint16_t cnt=1000;cnt>0;cnt--)
			{
				__HAL_TIM_SetCompare(&htim14,TIM_CHANNEL_1,cnt);
				HAL_Delay(1);
			}
			printf("check6\r\n");
			HAL_Delay(1000);
		}
		if(buffer[0]==4)
		{
			printf("check7\r\n");
			HAL_TIM_Base_Start_IT(&htim2);
			printf("check8\r\n");
			HAL_Delay(1000);
		}
		if(buffer[0]==5)
		{
			printf("check9\r\n");
			for(uint16_t cnt=500;cnt<=2500;cnt++)
			{
				__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,cnt);
				HAL_Delay(1);
			}
			for(uint16_t cnt=2500;cnt>=500;cnt--)
			{
				__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,cnt);
				HAL_Delay(1);
			}
			printf("check10\r\n");
			HAL_Delay(1000);
		}
		if(buffer[0]==6)
		{
			printf("check11\r\n");
			if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==RESET)
			{
				HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_SET);
				
				printf("check12\r\n");
			}
			if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_4)==SET)
			{
				HAL_GPIO_WritePin(GPIOF,GPIO_PIN_9,GPIO_PIN_RESET);
				
				printf("check13\r\n");
			}
			if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==RESET)
			{
				
				HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_SET);
				printf("check14\r\n");
			}
			if(HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3)==SET)
			{
				
				HAL_GPIO_WritePin(GPIOF,GPIO_PIN_10,GPIO_PIN_RESET);
				printf("check15\r\n");
			}
			printf("check16\r\n");
			HAL_Delay(1000);
		}
		if(buffer[0]==7)																																																																																																																																																						)
		{
			printf("check17\r\n");
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_8,GPIO_PIN_SET);
			HAL_Delay(500);
			HAL_GPIO_WritePin(GPIOF,GPIO_PIN_8,GPIO_PIN_RESET);
			HAL_Delay(500);
			printf("check18\r\n");
		}
		/*if(buffer[0]!='\0')
		{
			printf("%s\r\n",buffer);
			memset(buffer,0,sizeof(buffer));
			printf("%d\r\n",len);
		}*/
		HAL_Delay(100);
		/*uint8_t  SendBuffer[30]={"hello,world!"};
    HAL_UART_Transmit(&huart2, (uint8_t *)SendBuffer, sizeof(SendBuffer), HAL_MAX_DELAY);*/
  }
  /* USER CODE END 3 */
}

4  舵机

4.1  相关计算

我们打开TIM3定时器,这个定时器是挂在APB1总线上的,根据配置好的时钟树可知

频率是84MHz 

然后为了方便计算,我们把分频系数设置成84-1,这样相当于1秒钟点1000000次

由于舵机的驱动信号是50Hz,周期是0.02s,所以在一个周期内会点20000次

舵机的转动角度是按照高电平的占空比来计算的

 

经过手算,我们可以大致推出角度和比较值之间的关系,舵机的难点也就在这了

4.2  接线

舵机                  单片机

信号线(黄)-------PA6(根据自己的定时器而定)

电源线(红)-------ST-LINK上的5V(9/10)

地线(棕)----------GND

5  PID控制算法

5.1  简介

PID控制中有P、I、D三个参数,PID即:Proportional(比例)、Integral(积分)、Differential(微分)的缩写。只有明白这三个参数的含义和作用才能完成控制器PID参数整定,让控制器到达最佳控制效果。

PID是经典的闭环控制算法,具有原理简单,易于实现,适用面广,控制参数相互独立,参数的选定比较简单等优点。

凡是需要将某一个物理量“保持稳定”的场合(比如维持平衡,稳定温度、转速等),PID都会派上大用场。

5.2  比例Proportional

比例控制器实际上就是个放大倍数可调的放大器,即△P=Kp×e,式中Kp为比例增益,即Kp可大于1,也可小于1;e为控制器的输入,也就是测量值与给定值之差,又称为偏差。

比例控制有个缺点,就是会产生余差,要克服余差就必须引入积分作用。

只有比例系数无法准确到达目标位置,但增大比例系数有利于减小误差

5.3  积分Integral

控制器的积分作用就是为了消除自控系统的余差而设置的。所谓积分,就是随时间进行累积的意思,即当有偏差输入e存在时,积分控制器就要将偏差随时间不断累积起来,也就是积分累积的快慢与偏差e的大小和积分速度成正比。只要有偏差e存在,积分控制器的输出就要改变,也就是说积分总是起作用的,只有偏差不存在时,积分才会停止。

实际上积分作用很少单独使用,通常与比例作用一起使用,使其既具有把偏差放大(或缩小)的比例作用,又具有将偏差随时间累积的积分作用,且其作用方向是一致的。这时控制器的输出为:△P=Ke+△Pi,式中△P为控制器输出值的变化;Ke为比例作用引起的输出;△Pi为积分作用引起的输出。

同时使用比例系数和积分可以准确到达目标位置,但当比例系数过大时会出现不稳定性

5.4  微分Differential

微分作用主要是用来克服被控对象的滞后,常用于温度控制系统。除采用微分作用外,在使用控制系统时要注意测量传送的滞后问题,如温度测量元件的选择和安装位置等。

在常规PID控制器中,微分作用的输出变化与微分时间和偏差变化的速度成比例,而与偏差的大小无关,偏差变化的速度越大,微分时间越长,则微分作用的输出变化越大。但如果微分作用过强,则可能由于变化太快而由其自身引起振荡,使控制器输出中产生明显的“尖峰”或“突跳”。为了避免这一扰动,在PID调节器和DCS中可使用微分先行PID运算规律,即只对测量值PV进行微分,当人工改变控制器的给定值SP时,不会造成控制器输出的突变,避免了改变SP的瞬间给控制系统带来的扰动。如TDC-3000,则在常规PID算法中增加一个软开关,组态时供用户选择控制器对偏差、还是测量值进行微分。

微分保证系统能够平稳运行,不至于出现太大的波动

5.5  公式

连续形式:

离散形式(常用,由连续形式推导):

5.6  积分限幅

试想一下,使用P和I项进行控制,无人机在地面准备起飞,期望高度是100m。此时一个人摁住无人机,那么误差值一直为100m,控制器P项输出保持不变,控制器I项输出是线性增长的

一段时间后,那个人突然不摁了,此时控制器I项输出可能非常大,再加上控制器P项输出,控制器给电机的输出非常大,那么无人机速度就非常快并且飞的非常高。对于这种情况,我们对积分限幅,I项增加到一定值时就不再增了。那么无人机在上面这种情况下就不至于飞的很快很高,这就是积分限幅的作用。

5.7  积分限行

我们直接将传感器的反馈值分出来一路给到控制器的D项,这样做有一个好处就是如果希望无人机快速反应移动,当期望高度突变的时候,影响会先给到P和I项,由于开始时无人机位置没有突变,传感器反馈的实际高度值也不会突变,因此不会马上给到D项,这样就有效减小了D项开始的缓冲作用,使无人机可以快速反应移动。如果不这样做,那么如果期望高度突变,D项的微分值也会突变变得很大,缓冲作用非常大,使相应效果大大降低。
 

5.8  相关代码

chassis_motor_pid[i].SpeedPID.current=motor_chassis[i].speed_rpm;
pid_calc(&chassis_motor_pid[i].SpeedPID);

void pid_calc(_pid* pid)
{
	pid->e=pid->target-pid->current;
	pid->p_out=(int32_t)(pid->Kp*pid->e);
	
	if(fabs(pid->e)<I_Band)//积分限幅和积分分离 
	{
		pid->i_out+=(int32_t)(pid->Ki*pid->e);
		limit(&(pid->i_out),pid->IntegralLimit);
	}
	else
	{
		pid->i_out=0;
	}
	
	pid->d_out=(int32_t)(pid->Kd*(pid->e-pid->last_e));
	
	pid->total_out=pid->p_out+pid->i_out+pid->d_out;//公式 
	
	limit(&(pid->total_out),pid->MaxOutput);//输出限制 
	pid->last_e=pid->e;//更新last值 
 } 

6  systick(定时器,多用于延时)

6.1  寄存器

Systick的寄存器分为:

        CTRL        

        LOAD

        VAL

        CALIB

        ENABLE位就是开关Systick

        TICKINT的作用是当写入的值为"1"时,会进入SysTick_Handler()中断服务函数,随后执行用户代码。

        CLKSOURCE该位要联合最上方的图来理解,当值为0时Systick的时钟频率为AHB/8也就是72MHz/8=9MHz,当值为1时Systick的时钟频率为AHB桥的频率。AHB桥的频率常用为72MHz。

        COUNTFLAG该位的作用就是告诉使用者(芯片程序)Systick已经计数完成,用户程序可以通过检测该位是否为"1"来判断Systick的计数状态。
 

 RELOAD该位是一个24位的寄存器,也就是说最大支持0xFFFFFF,同时还是能够自动重装载值,自动重装载的理解就是当RELOAD倒数至0后,能够重新将用户预设值填充,比如说预设值为0xFF,那么当倒数至0时,它能够自动将0xFF填充进RELOAD。

 CURRENT该位是与RELOAD相对应的,它的值就等于RELOAD的值,只不过当我们对该寄存器进行写操作时,会强行将RELOAD的值清0,同时会将CTRL->COUNTFLAG的标志位置0。读操作则只是返回RELOAD的值。

 6.2  代码

static uint8_t us = 0;
static uint16_t ms = 0;
 
void SysTickConfig(void)
{
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
	
	us = SystemCoreClock / 8000000;
	
	ms = (uint16_t )us *1000;
}
 
void delay_us(uint32_t nus)
{
	uint32_t flags;
	SysTick->LOAD 	 = nus * us;
	SysTick->VAL	 = 0x00;
	SysTick->CTRL	|= SysTick_CTRL_ENABLE_Msk;
	do
	{
		flags = SysTick->CTRL;
	}while((flags&0x01)&&!(flags&(1<<16)));
	SysTick->CTRL	&= ~SysTick_CTRL_ENABLE_Msk;
	SysTick->VAL	 = 0x00;
}
 
void delay_ms(uint16_t nms)
{
	uint32_t flags;
	SysTick->LOAD 	 = (uint32_t )nms * ms;
	SysTick->VAL	 = 0x00;
	SysTick->CTRL	|= SysTick_CTRL_ENABLE_Msk;
	do
	{
		flags = SysTick->CTRL;
	}while((flags&0x01)&&!(flags&(1<<16)));
	SysTick->CTRL	&= ~SysTick_CTRL_ENABLE_Msk;
	SysTick->VAL	 = 0x00;
}

 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8)

 配置CTRL->CLKSOURCE,将其选择为外部时钟源。

 us = SystemCoreClock / 8000000

        这里是整个Systick的重点,定时器准不准就看这里的参数,SystemCoreClock 是系统时钟频率,这里取72MHz,同时又由于上方代码进行了一个8分频,所以需要除以8。但如果单单只是/8,那么此时一个us应该为1s,而Systick的计数速度是由时钟频率决定的,此时时钟频率为9MHz,代表计数一秒需要数9M个数,而1s中有1M个us,所以用9M/1M就等于Systick计时1us需要数的数,完整的式子应该是 us = (72,000,000 / 8) / 1,000,000=9

ms = (uint16_t )us *1000

        这里不难理解,1ms就是等于1000us,前面加个强制类型转换是为了让数据类型匹配上,避免编译器优化,造成数据丢失。

SysTick->LOAD      = nus * us

        此处就是给LOAD->RELOAD填充计数值自动重装载值,切记此处最大不超过24位。

SysTick->VAL     = 0x00 

        这里是为了清空计数器,确保计数精准。

SysTick->CTRL    |= SysTick_CTRL_ENABLE_Msk

         启动Systick。

    do
    {
        flags = SysTick->CTRL;
    }while((flags&0x01)&&!(flags&(1<<16)));

    此处作用是等待计数完成,flags&(1<<16)这里就是检测计数完成COUNTFLAG标志位是否为'1'

 SysTick->CTRL    &= ~SysTick_CTRL_ENABLE_Msk 

        关闭Systick。

  SysTick->VAL     = 0x00 

        再次清空计数器。

        下方的delay_ms()的原理同上方一样,只不过是将SysTick->LOAD      = nus * us 的us换成ms,SysTick->LOAD      = nms * ms ,当使用delay_ms()时,最大延时建议不超过1500ms
 

 7  蓝牙模块

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的位置上(蓝牙模块的用处就是可以无线调试而不用电脑上的虚拟串口调试) (注意不要按按钮)

我们在手机上安装蓝牙串口调试软件,并配对(注意在这之前手机的设置->蓝牙里已经连接成功)

然后这时会发现灯处于长灭状态(偶尔亮一会),就可以正常使用了,我们在手机里输入数据,跟原来用电脑的虚拟串口一模一样

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜只因C

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

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

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

打赏作者

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

抵扣说明:

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

余额充值