嵌入式开发STM32 -- 江协科技笔记

news2025/6/2 1:52:51

1.背景介绍及基础认知

8大输入输出

斯密特触发器:高于设定阈值输出高电平,低于设定阈值输出低电平

有关上拉输入、下拉输入、推挽输出、开漏输出、复用开漏输出、复用推挽输出以及浮空输入、模拟输入的区别

1、上拉输入:上拉就是把电位拉高,比如拉到Vcc。上拉就是将IO口上不确定的信号通过一个上拉电阻把IO上拉为高电平!电阻同时起限流作用!弱强只是上拉电阻的阻值不同,没有什么严格区分。

2、下拉输入:就是把电压拉低,拉到GND。与上拉原理相似。

3、浮空输入:浮空(floating)就是逻辑器件的输入引脚即不接高电平,也不接低电平。由于逻辑器件的内部结构,当它输入引脚悬空时,相当于该引脚接了高电平。一般实际运用时,引脚不建议悬空,易受干扰。 通俗讲就是让管脚什么都不接,浮空着。

4、模拟输入:模拟输入是指传统方式的输入.数字输入是输入PCM数字信号,即0,1的二进制数字信号,通过数模转换,转换成模拟信号,经前级放大进入功率放大器,功率放大器还是模拟的。

5、推挽输出:可以输出高,低电平,连接数字器件; 推挽结构一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。

6、开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内)。

7、复用开漏输出、复用推挽输出:可以理解为GPIO口被用作第二功能时的配置情况(即并非作为通用IO口使用)。

在STM32中选用IO模式,下面是参考网上的总结一下。
(1) 浮空输入_IN_FLOATING ——浮空输入,可以做KEY识别,RX
(2)带上拉输入_IPU——IO内部上拉电阻输入
(3)带下拉输入_IPD—— IO内部下拉电阻输入
(4) 模拟输入_AIN ——应用ADC模拟输入,或者低功耗下省电
(5)开漏输出_OUT_OD ——IO输出0接GND,IO输出1,悬空,需要外接上拉电阻,才能实现输出高电平。当输出为1时,IO口的状态由上拉电阻拉高电平,但由于是开漏输出模式,这样IO口也就可以由外部电路改变为低电平或不变。可以读IO输入电平变化,实现C51的IO双向功能。
(6)推挽输出_OUT_PP ——IO输出0-接GND, IO输出1 -接VCC,读输入值是未知的
(7)复用功能的推挽输出_AF_PP ——片内外设功能(I2C的SCL,SDA)

(8)复用功能的开漏输出_AF_OD——片内外设功能(TX1,MOSI,MISO.SCK.SS)

1.1.内核划分

A: 应用在手机 R: 实时性很高的领域 M: 应用在单片机

1.2.外设

1.3.在线安装支持包

2.新建工程文件

2.1.正确插线

2.2.添加必要的工程文件

2.2.1.在工程文件下新建一个Start文件夹,讲必要的工程文件添加到此文件夹

2.2.2.添加头文件路径 

2.2.3.创建User 文件夹创建Main函数

2.2.4.添加库文件 :工程下创建Library文件夹,把库函数都添加进去,创建Library组,在添加Library下的现有项

2.2.5.复制到工程User文件夹,再在Keil下添加现有项

2.2.6.定义宏配置工程文件

2.2.7.添加library和User头文件的路径

2.2.8.如何选择启动文件 

2.2.9.新建工程的步骤 

2.2.10.工程架构

3.GPIO

3.1.元气件两端有高低电平才可以形成电流:上:单片机低电平形成电流 下:单片机高电平形成电流

3.2.二极管:PN结

二极管有颜色的一极为负极:P为正极,N为负极;电流只能从正极流到负极,电子只能从负极客流到正极 

3.2.1.整流桥

3.3.三极管:NPN

PNP:

3.3.1.三极管发射极和集电极为什么不能互换

3.3.2.三极管连接的元器件位置(漏电问题):防止基极的小电流流过

3.3.3.开关电路

3.4.面包板的构造

3.5.清除工程文件

4.GPIO输入输出

4.1.RCC、GPIO最常用的3个函数

RCC简介:RCC是Reset and Clock Control (复位和时钟控制)的缩写,它是STM32内部的一个重要外设,负责管理各种时钟源和时钟分频,以及为各个外设提供时钟使能。RCC模块可以通过寄存器操作或者库函数来配置。

4.2.使用GPIO的函数点亮和熄灭灯

	//设置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//输入低电平
	//GPIO_ResetBits(GPIOA,GPIO_Pin_0);
	//输入高电平
	//GPIO_SetBits(GPIOA, GPIO_Pin_0);
	
	//输入低电平
	//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);
	//输入高电平
	GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);

4.3.使用延时函数达到使用灯闪烁的效果

4.3.1.在工程添加延时函数库

电路将LED接到A0

代码

int main(void)
{
	//设置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	while(1)
	{
		//输入低电平
		//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);
		Delay_ms(100);
		//输入高电平
		GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
		Delay_ms(100);
	}
		return 0;
}

4.4.蜂鸣器

4.4.1.VCC/VSS/VEE等含义

一文读懂电路中VCC、VDD、VEE、VSS的区别 - 知乎 (zhihu.com)

在电子电路设计中,VCC、VDD、VEE和VSS是常见的电源和地线标识,它们各自代表不同的电源电压和地线类型。理解这些术语的区别对于正确设计和分析电路至关重要。

VCC

  • 定义:VCC是“Collector Voltage”或“Circuit Voltage”的缩写,通常用于双极型晶体管(如NPN晶体管)的集电极电源电压。在数字电路中,VCC通常指代正电源电压,用于为电路提供所需的正电压。
  • 应用:VCC广泛应用于模拟电路和数字电路中,提供电路所需的正电压。例如,在NPN晶体管电路中,VCC为正电压。
  • 特点:VCC通常用于模拟电源,提供稳定的电压。

VDD

  • 定义:VDD是“Drain Voltage”或“Device Voltage”的缩写,主要用于MOS晶体管和CMOS电路。它表示器件内部的工作电压。
  • 应用:VDD常见于集成电路(IC)和数字电路中,提供芯片的正电源电压。例如,在CMOS电路中,VDD通常连接到PMOS晶体管的源极。
  • 特点:VDD通常用于数字电源,提供芯片内部的工作电压。

VEE

  • 定义:VEE是“Emitter Voltage”或“Emitter-Emitter Voltage”的缩写,通常用于ECL电路的负电源电压。
  • 应用:VEE一般用于模拟电路中,提供负电源电压。例如,在PNP型晶体管电路中,VEE表示连接到发射极的电源电压。
  • 特点:VEE在放大电路中较为常见,用于提供对称电源。

VSS

  • 定义:VSS是“Source Voltage”或“Supply Voltage”的缩写,表示电源地或0V。
  • 应用:VSS通常用于数字电路中,作为电路的参考点或接地。在CMOS电路中,VSS指负电源。
  • 特点:VSS是电路的公共接地端,用于消除噪声和提供参考电平。

总结

  • 电压极性:VCC和VDD均表示正电源引脚,而VEE表示负电源引脚。VSS则代表接地引脚,不涉及电压极性。
  • 应用领域:VCC和VDD主要用于数字电路中,提供操作所需的正电压;VEE通常用于模拟电路中,提供负电压;VSS则用于连接电路到地,确保电路工作正常。
  • 传统用法:VCC和VSS这对术语通常用于晶体管和集成电路中;VDD和VEE则更常见于模拟电路设计中。

通过理解VCC、VDD、VEE和VSS的区别,可以更好地设计和调试电子设备和电路,确保电路的稳定性和可靠性。

代码:设置时钟 初始化对应接口 使用延时函数

	//蜂鸣器
	int main()
	{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
		
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB, &GPIO_InitStructure);
		
		while(1)
		{
			GPIO_ResetBits(GPIOB, GPIO_Pin_13);
			Delay_ms(100);
			GPIO_SetBits(GPIOB, GPIO_Pin_13);
			Delay_ms(100);
			GPIO_ResetBits(GPIOB, GPIO_Pin_13);
			Delay_ms(100);
			GPIO_SetBits(GPIOB, GPIO_Pin_13);
			Delay_ms(700);
		}
		return 0;
	}

4.4.分压电路

R1变小U0变大,R2变小U0变小

4.5.每一个开关控制一个LED的点亮和熄灭

线路图

 main

	//开关
	uint8_t keyNum;
	int main()
	{
		LED_Init();
		Key_Init();
		LED1_Off();
		LED2_Off();
		while(1)
		{
			keyNum = GetKeyNum();
			if(keyNum == 1)
				LED1_turn();
			if(keyNum == 2)
				LED2_turn();
		}
		return 0;
	}

LED函数:LED取反:读取对应端口的高低电平,再设置取反的电平

#include "stm32f10x.h"   

void LED_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
}

//LED的熄灭和点开
void LED1_ON()
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED1_Off()
{
	GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
void LED2_ON()
{
	GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
void LED2_Off()
{
	GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
void LED1_turn()
{
	//如果为低电平变为高电平
	if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)
		GPIO_SetBits(GPIOA, GPIO_Pin_1);	
	else
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
void LED2_turn()
{
	//如果为低电平变为高电平
	if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)
		GPIO_SetBits(GPIOA, GPIO_Pin_2);	
	else
		GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}

Key函数:按下/松开开关会有抖动,使用延迟函数消除

#include "stm32f10x.h"   
#include "Delay.h"

void Key_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = 	GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t GetKeyNum()
{
	uint8_t keyNum = 0;
	//LED2
	if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11 ) == 0 )
	{
		//消除抖动
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);//等松手
		//消除抖动
		Delay_ms(20);
		keyNum = 2;
	}
	//LED1
	if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 ) == 0 )
	{
		//消除抖动
		Delay_ms(20);
		while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);//等松手
		//消除抖动
		Delay_ms(20);
		keyNum = 1;
	}
	
	return keyNum;
}

4.6. 光敏电阻控制蜂鸣器

电路图

main 

	//关敏电阻驱动蜂鸣器
	int main()
	{
		Buzzer_Init();
		LightSensor_Init();
		Buzzer_Off();
		while(1)
		{
			if(GetLightSenNum() == 1)
				Buzzer_ON();
			else
				Buzzer_Off();
		}
		return 0;
	}

Lightsensor 

#include "stm32f10x.h"   
#include "Delay.h"

void LightSensor_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = 	GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

uint8_t GetLightSenNum()
{
	return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}

 Buzzer

#include "stm32f10x.h"   

void Buzzer_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

void Buzzer_ON()
{
	GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_Off()
{
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_turn()
{
	//如果为低电平变为高电平
	if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)
		GPIO_SetBits(GPIOB, GPIO_Pin_12);	
	else
		GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}

 4.7.OLED调试

OLED驱动函数

接线线路图

 4.6.1.调试

5.EXIT外部中断

5.1.中断的理论

不能使用pin相同的端口:例,PA1、PB1

5.2.对射式红外传感器

配置中断:初始化配置此4项

AFIO没有专门的库,内容和GPIO写在GPIO.h库内

NVIC函数在杂项文件内

main

	//对射式红外传感器
	int main()
	{
		CountSensor_Init();
		OLED_Init();
		OLED_ShowString(1, 1, "Count:");
		while(1)
		{
			uint16_t countSensor_Count = CountSensor_Get();
			OLED_ShowNum(1, 7, countSensor_Count, 5);
		}
		return 0;
	}

countSensor

#include "stm32f10x.h"    

uint16_t countSensor_Count = 0;
void CountSensor_Init()
{
	//设置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	//初始化端口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,未检测到输入时默认为高电平
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输入不需要速度
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置外部中断引脚选择
	
	//配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;//那个端口
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉触发:遮挡时触发,上拉触发:遮挡移开后触发
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
}

void EXTI15_10_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line14) == SET)//检测14端口,是否发生异常
	{
		countSensor_Count++;
		EXTI_ClearITPendingBit(EXTI_Line14);//清除标志位,防止死循环
	}
}

uint16_t CountSensor_Get()
{
	return countSensor_Count;
}

5.3.编码器计次

电路图

main

	int16_t encoderCount = 0;
	//编码器改变数值
	int main()
	{
		Encoder_Init();
		OLED_Init();
		OLED_ShowString(1, 1, "Count:");
		while(1)
		{
			encoderCount += EncoderNum_Get();
			OLED_ShowSignedNum(1, 7, encoderCount, 5);
		}
		return 0;
	}

encoder

#include "stm32f10x.h"    

int16_t encoderNum = 0;
void Encoder_Init()
{
	//设置时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	//初始化端口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入,未检测到输入时默认为高电平
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输入不需要速度
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//配置外部中断引脚选择
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
	
	//配置EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//那个端口
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉触发:遮挡时触发,上拉触发:遮挡移开后触发
	EXTI_Init(&EXTI_InitStructure);
	
	//配置NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选择通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选择通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
}

void EXTI0_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET)//检测0端口,是否发生异常
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
			encoderNum--;
		EXTI_ClearITPendingBit(EXTI_Line0);//清除标志位,防止死循环
	}
}
void EXTI1_IRQHandler()
{
	if(EXTI_GetITStatus(EXTI_Line1) == SET)//检测1端口,是否发生异常
	{
		if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
			encoderNum++;
		EXTI_ClearITPendingBit(EXTI_Line1);//清除标志位,防止死循环
	}
}

uint16_t EncoderNum_Get()
{
	int16_t tmp = encoderNum;
	encoderNum =0;
	return tmp;
}

6.定时器

TIM ( Timer )定时器

  • 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
  • 16 位计数器、预分频器、自动重装寄存器的时基单元,在 72MHz 计数时钟下可以实现最大 59.65s 的定时
  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入 捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

基础定时器:只有向上计时模式

通用定时器:向上计数、向下计数、中央对齐模式

6.1.定时器内部中断和定时器外部中断

time.h 

	#include "stm32f10x.h"  
	//定时器内部中断
//	void Time_Init()
//	{
//		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定时器1时钟开启
//		TIM_InternalClockConfig(TIM2);
//		
//		//初始化时基单位
//		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
//		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//时钟模式
//		TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//预分频器,此值和下值上限都为2^16
//		TIM_TimeBaseInitStructure.TIM_Prescaler =7200 - 1;//自动重装器
//		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计时器,高级定时器才需要
//		TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
//		
//		//清除一下标志位,防止一上电就进中断
//		TIM_ClearFlag(TIM2, TIM_FLAG_Update);
//		
//		TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中断配置
//		
//		//NVIC中断配置
//		NVIC_InitTypeDef NVIC_InitStructure;
//		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
//		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//		NVIC_Init(&NVIC_InitStructure);
//		
//		TIM_Cmd(TIM2, ENABLE);//使能控制
//	}
	
	//定时器外部中断
	void Time_Init()
	{
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定时器1时钟开启
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
		GPIO_InitTypeDef GPIO_Initstructure;
		GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IPU;
		GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0;
		GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
		
		TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);
		
		
		//初始化时基单位
		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
		TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频
		TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//时钟模式
		TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//预分频器,此值和下值上限都为2^16
		TIM_TimeBaseInitStructure.TIM_Prescaler =1 - 1;//自动重装器
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计时器,高级定时器才需要
		TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
		
		//清除一下标志位,防止一上电就进中断
		TIM_ClearFlag(TIM2, TIM_FLAG_Update);
		
		TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中断配置
		
		//NVIC中断配置
		NVIC_InitTypeDef NVIC_InitStructure;
		NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
		NVIC_Init(&NVIC_InitStructure);
		
		TIM_Cmd(TIM2, ENABLE);//使能控制
	}

main.h

	//时钟中断
	uint32_t num = 0; 
	int main()
	{
		OLED_Init();
		Time_Init();
		while(1)
		{
			OLED_ShowNum(1, 5, num, 5);
			OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);//获取自动重装值
		}
		return 0;
	}

	void TIM2_IRQHandler()//对应的中断函数
	{
		if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET )
		{
			TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
			num++;
		}
	}	

6.2. PWM

6.2.1.PWM的基础概念

OC ( Output Compare )输出比较 

  • 输出比较可以通过比较 CNT(计数器) 与 CCR(捕获比较寄存器) 寄存器值的关系,来对输出电平进行置1 、置0或翻转的操作,用于输出一定频率和占空比的 PWM 波形
  • 每个高级定时器和通用定时器都拥有 4 个输出比较通道
  • 高级定时器的前 3 个通道额外拥有死区生成和互补输出的功能

计算一个频率为1KHZ、占空比为50%、分辨率为1%的ARR、CCR、PSC 

6.2.2.呼吸灯
#include "stm32f10x.h"

void PWM_Init()
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//选择时钟
	TIM_InternalClockConfig(TIM2);
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	//
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High极性不翻转 TIM_OCPolarity_Low极性翻转
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
	TIM_OCInitStructure.TIM_Pulse = 100; //CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

//修改CCR的值
void PWM_SetCompare1(uint32_t compare)
{
	TIM_SetCompare1(TIM2, compare);
}
	void LightClink(int ms1, int ms2)
	{
		PWM_SetCompare1(100);
		Delay_ms(ms1);
		PWM_SetCompare1(0);
		Delay_ms(ms2);
	}
	//时钟内部/外部中断
	int main()
	{
		PWM_Init();
		while(1)
		{
			LightClink(2000, 1000);
			LightClink(2000, 1000);
			LightClink(2000, 1000);
			LightClink(1000, 1000);
			LightClink(1000, 1000);

//			for(int i = 0; i <=100; i++)
//			{
//				PWM_SetCompare1(i);
//				Delay_ms(40);
//			}
//			PWM_SetCompare1(0);
//			Delay_ms(40);
//			for(int i =0; i < 100 ; i++)
//			{
//				PWM_SetCompare1(100 - i);
//				Delay_ms(40);
//			}
//			PWM_SetCompare1(100);
//			Delay_ms(20);
		}
		return 0;
	}
6.2.3.端口重映射
	//重映射端口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO);
	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);

6.2.4.PWM操作舵机

void PWM_Init()
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//选择时钟
	TIM_InternalClockConfig(TIM2);
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;
	TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStrucure.TIM_Period = 20000 - 1;
	TIM_TimeBaseInitStrucure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);
	
	//输出比较
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);

	TIM_Cmd(TIM2, ENABLE);
}

//修改CCR的值
void PWM_SetCompare2(uint16_t compare)
{
	TIM_SetCompare2(TIM2, compare);
}
void Set_Angle(int angle)
{
	PWM_SetCompare2(angle * 2000 / 180 + 500);
}

	uint8_t keyNum ;
	int angle;
		
	//时钟内部/外部中断
	int main()
	{
		PWM_Init();
		Key_Init();
		OLED_Init();
		while(1)
		{

			keyNum = GetKeyNum();
			if(keyNum == 1)
			{
				angle += 30;
				if(angle > 180)
					angle = 0;
			}
			Set_Angle(angle);
			OLED_ShowString(1, 1, "angle:");
			OLED_ShowNum(1, 7, angle, 3);
			OLED_ShowString(2, 1, "num:");
			OLED_ShowNum(2, 5, keyNum, 1);


		}
		return 0;
	}
6.4.3.PWM操作电机

	int8_t speed;
	int8_t keyNum;
	//PWM操作电机
	int main()
	{
		PWM_Init();
		Key_Init();
		OLED_Init();
		while(1)
		{
			keyNum = GetKeyNum();
			if(keyNum == 1)
			{
				speed += 20;
				if(speed > 100) speed = -100;
				Set_MotorSpeed(speed);
			}
			OLED_ShowString(1,1,"Speed:");
			OLED_ShowSignedNum(1, 7, speed, 4);
		}
		return 0;
	}
void PWM_Init()
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//选择时钟
	TIM_InternalClockConfig(TIM2);
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;
	TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStrucure.TIM_Period = 100 - 1;
	TIM_TimeBaseInitStrucure.TIM_Prescaler = 36 - 1;
	TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);
	
	//输出比较
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);

	TIM_Cmd(TIM2, ENABLE);
}

//修改CCR的值
void PWM_SetCompare3(uint16_t compare)
{
	TIM_SetCompare3(TIM2, compare);
}
void Set_MotorSpeed(int8_t speed)
{
	if(speed >= 0)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_4);
		GPIO_ResetBits(GPIOA, GPIO_Pin_5);
		PWM_SetCompare3(speed);
	}
	else
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_5);
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);
		PWM_SetCompare3(-speed);//speed当前小于0		
	}
}
6.4.4. 输入捕获测频率和占空比

输入捕获流程

输入捕获检测方法

PSC数值修改 

	//输入捕获测频率
	int main()
	{
		PWM_Init();
		IC_Init();
		OLED_Init();
		PWM_SetCompare1(0);
		PWM_SetPrescaler(720 - 1);
		OLED_ShowString(1, 1, "Freq:00000Hz");
		OLED_ShowString(2, 1, "Duty:00%");
		while(1)
		{
			OLED_ShowNum(1, 6, GetFreq(), 5);
			OLED_ShowNum(2, 6, GetDuty(), 2);
		}
		return 0;
	}

#include "stm32f10x.h"

void PWM_Init()
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//选择时钟
	TIM_InternalClockConfig(TIM2);
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	//
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High极性不翻转 TIM_OCPolarity_Low极性翻转
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能
	TIM_OCInitStructure.TIM_Pulse = 100; //CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

//输入捕获测频率和占空比

void IC_Init()
{
	//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	//选择时钟
	TIM_InternalClockConfig(TIM3);
	
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	//配置通道1
	TIM_ICInitTypeDef TIM_ICInitstructure;
	TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;
	TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰
	TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//边沿检测、极性选择;上沿触发
	TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器
	TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//数据选择器,直连输入
	TIM_ICInit(TIM3, &TIM_ICInitstructure);
	
	//配置通道2
	TIM_PWMIConfig(TIM3, &TIM_ICInitstructure);//等同于下面的代码,函数内部进行ifelse判断
	TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;
	TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰
	TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//边沿检测、极性选择;上沿触发
	TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器
	TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//数据选择器,交叉输入
	TIM_ICInit(TIM3, &TIM_ICInitstructure);	
	
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//选择触发源
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//选择从模式
	
	TIM_Cmd(TIM3, ENABLE);
}

//修改CCR的值
void PWM_SetCompare1(uint16_t compare)
{
	TIM_SetCompare1(TIM2, compare);
}
//修改PSC
void PWM_SetPrescaler(uint16_t prescaler)
{
	TIM_PrescalerConfig(TIM2, prescaler, TIM_PSCReloadMode_Immediate);
}

//获取频率
uint32_t GetFreq()
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}

//获取占空比
uint32_t GetDuty()
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}
6.4.5.编码器接口
		//输入捕获测频率
	int main()
	{
		Encoder_Init();
		OLED_Init();
		OLED_ShowString(1, 1, "CNT:");
		while(1)
		{
			OLED_ShowSignedNum(1, 5, Get_Encoder(), 5);
			Delay_ms(1000);
		}
		return 0;
	}
	
void Encoder_Init()
{
	
		//打开时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	
	//初始化GPIO
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter =  0;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	//配置通道1
	TIM_ICInitTypeDef TIM_ICInitstructure;
	TIM_ICStructInit(&TIM_ICInitstructure);
	TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;
	TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰
	
	TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;
	TIM_ICStructInit(&TIM_ICInitstructure);
	TIM_ICInitstructure.TIM_ICFilter = 0xf;//滤波器,参数数值增大可以消除噪音和毛刺的干扰

	TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
	
	TIM_Cmd(TIM3, ENABLE);
}

int16_t Get_Encoder()
{
	int tmp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3, 0);
	return tmp;
}

7.AD模数转换

7.1.AD的概念

4种转换模式

  • 单次转换, 非 扫 描模式
  • 连续转换,非扫描模式
  • 单次转换,扫描模式
  • 连续转换, 扫描模式

//	//AD
//	uint16_t ADValue;
//	float voltage;
//	int main()
//	{
//		OLED_Init();
//		OLED_ShowString(1 ,1 , "ADValue:");
//		OLED_ShowString(2 ,1 , "Voltage:0.00V");
//		AD_Init();
//		while(1)
//		{
//			ADValue = AD_GetValue();
//			voltage = (float)ADValue / 4095 * 3.3;//需先转为浮点数进行除法运算
//			
//			OLED_ShowNum(1, 9, ADValue, 5);
//			OLED_ShowNum(2, 9, voltage, 1);
//			OLED_ShowNum(2, 11, (uint16_t)( voltage * 100 ) % 100, 2);
//			
//		}
//		return 0;
//	}
	
	//多AD
	uint16_t ad1, ad2, ad3, ad4;
	int main()
	{
		OLED_Init();
		OLED_ShowString(1 ,1 , "ad1:");
		OLED_ShowString(2 ,1 , "ad2:");
		OLED_ShowString(3 ,1 , "ad3:");
		OLED_ShowString(4 ,1 , "ad4:");
		AD_Init();
		while(1)
		{
			ad1 = AD_GetValue(ADC_Channel_0);
			ad2 = AD_GetValue(ADC_Channel_1);
			ad3 = AD_GetValue(ADC_Channel_2);
			ad4 = AD_GetValue(ADC_Channel_3);
			
			OLED_ShowNum(1, 5, ad1, 5);
			OLED_ShowNum(2, 5, ad2, 5);
			OLED_ShowNum(3, 5, ad3, 5);
			OLED_ShowNum(4, 5, ad4, 5);
		}
		return 0;
	}
#include "stm32f10x.h"

//void AD_Init()
//{
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//	
//	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz
//	
//	//配置GPIO口
//	GPIO_InitTypeDef GPIO_InitStructure;
//	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
//	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
//	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//	GPIO_Init(GPIOA, &GPIO_InitStructure);
//	
//	//规则组通道配置
//	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定
//	
//	//初始化ADC
//	ADC_InitTypeDef ADC_InitStructure;
//	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式
//	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
//	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发
//	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续模式
//	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式
//	ADC_InitStructure.ADC_NbrOfChannel = 1;
//	ADC_Init(ADC1, &ADC_InitStructure);
//	
//	//运行控制
//	ADC_Cmd(ADC1, ENABLE);
//	
//	//校准复位
//	ADC_ResetCalibration(ADC1);
//	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//	ADC_StartCalibration(ADC1);
//	while(ADC_GetCalibrationStatus(ADC1) == SET);
//}

//uint16_t AD_GetValue()
//{
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
//	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待
//	return ADC_GetConversionValue(ADC1);//获取
//}


//连续转换模式
//void AD_Init()
//{
//	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续模式
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//连续转换模式,只需要触发启动一次
//}

//uint16_t AD_GetValue()
//{
//	return ADC_GetConversionValue(ADC1);//获取
//}


//非连续非扫描实现多AD
void AD_Init()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz
	
	//配置GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//规则组通道配置
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定
	
	//初始化ADC
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续模式
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描模式
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	//运行控制
	ADC_Cmd(ADC1, ENABLE);
	
	//校准复位
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1) == SET);
}

uint16_t AD_GetValue(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
	while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待
	return ADC_GetConversionValue(ADC1);//获取
}

8.DMA ( Direct Memory Access )直接存储器存取

DMA数据转运

	//DMA数据转运
	uint8_t arr1[] = {0x12, 0x23, 0xa1, 0x3b};
	uint8_t arr2[4];
	int main()
	{
		OLED_Init();
		MyDMA_Init((uint32_t)arr1, (uint32_t)arr2, 4);//数据转运
		while(1)
		{
			OLED_ShowString(1, 1, "arr1:");
			OLED_ShowString(3, 1, "arr2:");
			OLED_ShowNum(1, 7, (uint32_t)arr1, 8); 
			OLED_ShowNum(3, 7, (uint32_t)arr1, 8); 
			
			OLED_ShowHexNum(2, 1, arr1[0], 2);
			OLED_ShowHexNum(2, 4, arr1[1], 2);
			OLED_ShowHexNum(2, 7, arr1[2], 2);
			OLED_ShowHexNum(2, 10, arr1[3], 2);
			Delay_s(1);	
			MyDMA_Transfer();
			arr1[0]++;
			arr1[1]++;
			arr1[2]++;
			arr1[3]++;
			
			OLED_ShowHexNum(4, 1, arr2[0], 2);
			OLED_ShowHexNum(4, 4, arr2[1], 2);
			OLED_ShowHexNum(4, 7, arr2[2], 2);
			OLED_ShowHexNum(4, 10, arr2[3], 2);
			Delay_s(1);
		}
		return 0;
	}
#include "stm32f10x.h"

//ADC数据转移
uint32_t mySize;
void MyDMA_Transfer()
{
	DMA_Cmd(DMA1_Channel1, DISABLE);//失能
	DMA_SetCurrDataCounter(DMA1_Channel1, mySize);//修改计数器
	DMA_Cmd(DMA1_Channel1, ENABLE);//使能
	DMA_GetFlagStatus(DMA1_FLAG_TC1);//等待转运完成
	DMA_ClearFlag(DMA1_FLAG_TC1);
}
void MyDMA_Init(uint32_t addrA, uint32_t addrB, uint32_t size)
{
	//时钟开启
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = addrA;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
	DMA_InitStructure.DMA_MemoryBaseAddr = addrB;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	
	mySize = size;
	DMA_InitStructure.DMA_BufferSize = mySize;//计数器
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是来源还是目的地
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否使用软件触发
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否自动重装
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	//DMA使能
	DMA_Cmd(DMA1_Channel1, DISABLE);
	MyDMA_Transfer();
}
	//DMA数据转运ADC
	int main()
	{
		OLED_Init();
		MyDMA_Init();//数据转运
		OLED_ShowString(1 ,1 , "ad1:");
		OLED_ShowString(2 ,1 , "ad2:");
		OLED_ShowString(3 ,1 , "ad3:");
		OLED_ShowString(4 ,1 , "ad4:");

		while(1)
		{
//			ADDMA_GetValue();
			OLED_ShowNum(1, 5, AD_Value[0], 5);
			OLED_ShowNum(2, 5, AD_Value[1], 5);
			OLED_ShowNum(3, 5, AD_Value[2], 5);
			OLED_ShowNum(4, 5, AD_Value[3], 5);

		}
		return 0;
	}
	

uint16_t AD_Value[4];

void MyDMA_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	//时钟开启
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz

	
	//配置GPIO口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//规则组通道配置
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根据引脚定义表选择 ADC_SampleTime_55Cycles5采样周期数值越小速度越快,数值越大数据转换越稳定
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);
	
	//初始化ADC
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立转换模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部源即软件触发
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式
	ADC_InitStructure.ADC_NbrOfChannel = 4;
	ADC_Init(ADC1, &ADC_InitStructure);
	

	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_BufferSize = 4;//计数器
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是来源还是目的地
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//是否使用软件触发
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//是否自动重装
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	//不可以在未使能前校准复位
//		//校准复位
//	ADC_ResetCalibration(ADC1);
//	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
//	ADC_StartCalibration(ADC1);
//	while(ADC_GetCalibrationStatus(ADC1) == SET);
	
	//DMA使能
	DMA_Cmd(DMA1_Channel1, ENABLE);
	ADC_DMACmd(ADC1, ENABLE);//开启DMA到ADC信号
	//ADC使能
	ADC_Cmd(ADC1, ENABLE);
	
		//校准复位
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1) == SET);
	
	//自动转换转运的情况下使用
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
}
void ADDMA_GetValue(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);//失能
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);//修改计数器
	DMA_Cmd(DMA1_Channel1, ENABLE);//使能
	

	ADC_SoftwareStartConvCmd(ADC1, ENABLE);//启动
	
	//等待转运完成
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

9.串口通信(USART) 

9.1.基础概率

9.2.串口通信

使用printf函数,先打开Use MicroLIB

//此函数是printf函数底层调用打印的函数
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}	
	//串口
	int main()
	{
		Serial_Init();
		//printf函数移植方法
		printf("Num = %d\r\n",666);
		char str[100];
		sprintf(str, "Num = %d\r\n", 666);//指定打印位置
		Serial_SendString(str);
		while(1)
		{

		}
		return 0;
	}

 串口接收

1.查询

	//串口发送
	int main()
	{
		Serial_Init();
		OLED_Init();
		while(1)
		{
			//查询
			if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
			{
				receiveData = USART_ReceiveData(USART1);
				//读取后标志位自动置空
				OLED_ShowHexNum(1, 1, receiveData, 2);
			}
		}
		return 0;
	}

2.中断

	//串口发送
	int main()
	{
		Serial_Init();
		OLED_Init();
		while(1)
		{
			//中断
			if(Serial_GetRxFLag() == 1)
			{
				receiveData = Serial_GetRxData();
				//读取后标志位自动置空
				OLED_ShowHexNum(1, 1, receiveData, 2);
				//回传
				Serial_SendByte(receiveData);
			}
		}
		return 0;
	}
uint8_t serial_RxData,serial_RxFlag;

uint8_t Serial_GetRxFLag(void)
{
	if(serial_RxFlag == 1)
	{
		serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData()
{
	return serial_RxData;
}

//串口接收和发送
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//开启gpio口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化串口
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;//效验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bit
	USART_Init(USART1, &USART_InitStructure);
	
	//中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_Initstructure;
	NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_Initstructure);
	
	USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void)
{
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
	{
		serial_RxFlag = 1;
		serial_RxData = USART_ReceiveData(USART1);
	}
}

9.3.串口通信数据包·

数据包

串口发送HEX数据包

	uint8_t receiveData;
	//串口发送HEX数据包
	int main()
	{
		Serial_Init();
		OLED_Init();
		Key_Init();
		serial_TxPacket[0] = 0x12;
		serial_TxPacket[1] = 0x23;
		serial_TxPacket[2] = 0x34;
		serial_TxPacket[3] = 0x45;
		
		uint8_t keyNum;//按键
		while(1)
		{
			keyNum = GetKeyNum();
			if(keyNum == 1)
			{
				serial_TxPacket[0]++;
				serial_TxPacket[1]++;
				serial_TxPacket[2]++;
				serial_TxPacket[3]++;
				
				Serial_SendPacket();
				
				OLED_ShowHexNum(2, 1, serial_TxPacket[0], 2); 
				OLED_ShowHexNum(2, 4, serial_TxPacket[1], 2); 
				OLED_ShowHexNum(2, 7, serial_TxPacket[2], 2); 
				OLED_ShowHexNum(2, 10, serial_T xPacket[3], 2); 
			}
			
			if(Serial_GetRxFLag())
			{
				OLED_ShowHexNum(1, 1, serial_RxPacket[0], 2); 
				OLED_ShowHexNum(1, 4, serial_RxPacket[1], 2); 
				OLED_ShowHexNum(1, 7, serial_RxPacket[2], 2); 
				OLED_ShowHexNum(1, 10, serial_RxPacket[3], 2); 
			}
		}
		return 0;
	}
//串口发送HEX数据包

uint8_t serial_RxPacket[4], serial_TxPacket[4];
uint8_t serial_Flag;

uint8_t Serial_GetRxFLag(void)
{
	if(serial_Flag == 1)
	{
		serial_Flag = 0;
		return 1;
	}
	return 0;
}
//发送数据包
void Serial_SendPacket()
{
	Serial_SendByte(0xFF);//报头
	Serial_SendArray(serial_TxPacket, 4);
	Serial_SendByte(0xFE);//报尾
}
//接受数据包


//串口接收和发送
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//开启gpio口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化串口
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;//效验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bit
	USART_Init(USART1, &USART_InitStructure);
	
	//中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_Initstructure;
	NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_Initstructure);
	
	USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void)
{
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
	{
		static uint8_t serial_State = 0;//当前状态
		static uint8_t pRxData = 0;//接受到第几个数据
		uint8_t rxData = USART_ReceiveData(USART1);//读取当前数据
		if(serial_State == 0){
			if(rxData == 0xFF)
			{
				serial_State = 1;
			}
		}else if (serial_State == 1){
			serial_RxPacket[pRxData] = rxData;
			pRxData++;
			if(pRxData == 4)
			{
				pRxData %= 4;
				serial_State = 2;
			}
		}else if(serial_State == 2){
			if(rxData == 0xFE)
			{
				serial_State = 0;//状态改变
				serial_Flag = 1;//标志位可读
			}
		}
		
	}
}

串口发送文本数据包

//串口发送文本数据包

char serial_RxPacket[100];
uint8_t serial_Flag;

uint8_t Serial_GetRxFLag(void)
{
	if(serial_Flag == 1)
	{
		serial_Flag = 0;
		return 1;
	}
	return 0;
}
//接受数据包


//串口接收和发送
void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//开启gpio口
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//初始化串口
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;//效验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位长度
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//报文8bit
	USART_Init(USART1, &USART_InitStructure);
	
	//中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_Initstructure;
	NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_Initstructure);
	
	USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void)
{
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
	{
		static uint8_t serial_State = 0;//当前状态
		static uint8_t pRxData = 0;//接受到第几个数据
		uint8_t rxData = USART_ReceiveData(USART1);//读取当前数据
		if(serial_State == 0){
			if(rxData == '@')
			{
				serial_State = 1;
				pRxData = 0;
			}
		}else if (serial_State == 1){
			if(rxData == '\r')
				serial_State = 2;
			else
			{
				serial_RxPacket[pRxData] = rxData;
				pRxData++;
			}
		}else if(serial_State == 2){
			if(rxData == '\n')
			{
				serial_State = 0;//状态改变
				serial_RxPacket[pRxData] = '\0';//c串以'\0'结尾
				serial_Flag = 1;//标志位可读
			}
		}
		
	}
}
	uint8_t receiveData;
	//串口发送文本数据包
	int main()
	{
		Serial_Init();
		OLED_Init();
		LED_Init();
		Key_Init();
		OLED_ShowString(1, 1, "TxPacket:");
		OLED_ShowString(3, 1, "RxPacket:");
		while(1)
		{
			if(Serial_GetRxFLag())
			{
				OLED_ShowString(4, 1, "                ");
				OLED_ShowString(4, 1, serial_RxPacket);
				if(strcmp(serial_RxPacket, "LED_ON") == 0)
				{
					LED1_ON();
					Serial_SendString("LED_ON\r\n");
					OLED_ShowString(2, 1, "                ");
					OLED_ShowString(2, 1, "LED_ON_OK");
				}
				else if(strcmp(serial_RxPacket, "LED_OFF") == 0)
				{
					LED1_Off();
					Serial_SendString("LED_OFF\r\n");
					OLED_ShowString(2, 1, "                ");
					OLED_ShowString(2, 1, "LED_OFF_OFF");
				}
				else
				{
					Serial_SendString("ERROR_COMMOD\r\n");
					OLED_ShowString(2, 1, "                ");
					OLED_ShowString(2, 1, "ERROR_COMMOD");
				}
			}
		}
		return 0;
	}

 10.I2C

10.I2C通信

指定地址读取/写入一片区域的时序

12.Unix时间戳

12.1.时间戳的基础概念

12.2.BKP、RTC概念

12.3.BKP读写数据

  • BKP掉电数据(主和备用电源不同时掉电)不丢失
uint16_t writeArray[] = {0x1234, 0x5678};
uint16_t readArray[2];
	//BKP读写数据
	int main()
	{
		OLED_Init();
		Key_Init();
		//初始化时钟PWR/BKP
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
		PWR_BackupAccessCmd(ENABLE);//备用电源访问使能
		
		OLED_ShowString(1, 1, "W:");
		OLED_ShowString(2, 1, "R:");
		uint8_t keyNum;
		while(1)
		{
			keyNum = GetKeyNum();
			//按键按下,写入数据++并写入
			if(keyNum == 1)
			{
				writeArray[0]++;
				writeArray[1]++;
				
				BKP_WriteBackupRegister(BKP_DR1, writeArray[0]);
				BKP_WriteBackupRegister(BKP_DR2, writeArray[1]);
				
				OLED_ShowHexNum(1, 3, writeArray[0], 4);
				OLED_ShowHexNum(1, 8, writeArray[1], 4);
			}
			//实时读取数据
			readArray[0] = BKP_ReadBackupRegister(BKP_DR1);
			readArray[1] = BKP_ReadBackupRegister(BKP_DR2);
			
			OLED_ShowHexNum(2, 3, readArray[0], 4);
			OLED_ShowHexNum(2, 8, readArray[1], 4);
		}
		return 0;
	}

 12.4.实时时间

	//读取RTC
	int main()
	{
		OLED_Init();
		MyRTC_Init();
		
		/*显示静态字符串*/
		OLED_ShowString(1, 1, "Date:XXXX-XX-XX");
		OLED_ShowString(2, 1, "Time:XX:XX:XX");
		OLED_ShowString(3, 1, "CNT :");
		OLED_ShowString(4, 1, "DIV :");
		while(1)
		{
			struct tm time_data = MyRTC_ReadTime();							//RTC读取时间,最新的时间存储到MyRTC_Time数组中
		
			OLED_ShowNum(1, 6, time_data.tm_year, 4);		//显示MyRTC_Time数组中的时间值,年
			OLED_ShowNum(1, 11, time_data.tm_mon, 2);		//月
			OLED_ShowNum(1, 14, time_data.tm_mday, 2);		//日
			OLED_ShowNum(2, 6, time_data.tm_hour, 2);		//时
			OLED_ShowNum(2, 9, time_data.tm_min, 2);		//分
			OLED_ShowNum(2, 12, time_data.tm_sec, 2);		//秒
			
			OLED_ShowNum(3, 6, RTC_GetCounter(), 10);	//显示32位的秒计数器
			OLED_ShowNum(4, 6, RTC_GetDivider(), 10);	//显示余数寄存器
		}
		return 0;
	}
#include "stm32f10x.h"
#include <time.h>

void MyRTC_SetTime(struct tm time_date)
{
	time_t time_cnt;		//定义秒计数器数据类型
	
	time_cnt = mktime(&time_date) - 8 * 60 * 60;	//调用mktime函数,将日期时间转换为秒计数器格式
													//- 8 * 60 * 60为东八区的时区调整
	RTC_SetCounter(time_cnt);						//将秒计数器写入到RTC的CNT中
	RTC_WaitForLastTask();							//等待上一次操作完成
}

struct tm MyRTC_ReadTime(void)
{
	time_t time_cnt;		//定义秒计数器数据类型
	struct tm time_date;	//定义日期时间数据类型
	
	time_cnt = RTC_GetCounter() + 8 * 60 * 60;		//读取RTC的CNT,获取当前的秒计数器
													//+ 8 * 60 * 60为东八区的时区调整
	
	time_date = *localtime(&time_cnt);				//使用localtime函数,将秒计数器转换为日期时间格式
	time_date.tm_year += 1900;
	time_date.tm_mon += 1;
	return time_date;
}

void MyRTC_Init(void)
{
	if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)			//通过写入备份寄存器的标志位,判断RTC是否是第一次配置
															//if成立则执行第一次的RTC配置
	{
		//初始化时钟,备用电源访问打开
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
		PWR_BackupAccessCmd(ENABLE);
		
		//时钟源配置,等待时钟准备好
		RCC_LSEConfig(RCC_LSE_ON);
		while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
		
		//选择时钟源
		RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
		RCC_RTCCLKCmd(ENABLE);
		
		//等待时钟同步和上一次写入程序结束
		RTC_WaitForSynchro();
		RTC_WaitForLastTask();
		
		//设定分频
		RTC_SetPrescaler(32768 - 1);//分频到1HZ
		RTC_WaitForLastTask();//每次操作要等待上一次操作结束
		
		//设置起始值
		struct tm time_data;
		time_data.tm_year = 2025 - 1900;
		time_data.tm_mon = 5 -1 ;
		time_data.tm_mday = 19;
		time_data.tm_hour = 5;
		time_data.tm_min = 30;
		time_data.tm_sec = 15;
		MyRTC_SetTime(time_data);
		//RTC_SetCounter(1747599833);
		
		RTC_WaitForLastTask();//每次操作要等待上一次操作结束
		BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);			//在备份寄存器写入自己规定的标志位,用于判断RTC是不是第一次执行配置
	}
	else													//RTC不是第一次配置
	{
		RTC_WaitForSynchro();								//等待同步
		RTC_WaitForLastTask();								//等待上一次操作完成
	}
}

13.电源控制

13.1.基础理论

13.2.修改主频 

只读文件,修改权限

	//修改主频
	int main()
	{
		OLED_Init();
		OLED_ShowString(1, 1, "SYSCLK:");
		OLED_ShowNum(1, 8, SystemCoreClock, 8);
		while(1)
		{
			OLED_ShowString(2, 1, "Running");
			Delay_ms(500);
			OLED_ShowString(2, 1, "       ");
			Delay_ms(500);
		}
		return 0;
	}

13.3.睡眠模式+串口收发

		uint8_t receiveData;
	//串口发送
	int main()
	{
		Serial_Init();
		OLED_Init();
		while(1)
		{
//			//查询
//			if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
//			{
//				receiveData = USART_ReceiveData(USART1);
//				//读取后标志位自动置空
//				OLED_ShowHexNum(1, 1, receiveData, 2);
//			}
			//中断
			if(Serial_GetRxFLag() == 1)
			{
				receiveData = Serial_GetRxData();
				//读取后标志位自动置空
				OLED_ShowHexNum(1, 1, receiveData, 2);
				//回传
				Serial_SendByte(receiveData);
				OLED_ShowHexNum(1, 1, receiveData, 2);
			}
			OLED_ShowString(2, 1, "Running");
			Delay_ms(100);
			OLED_ShowString(2, 1, "       ");
			Delay_ms(100);
			__WFI();
		}
		return 0;
	}
	

 13.4.停机模式

  • 进入停止模式后会使用HSI时钟,需要重新选择时钟

	//对射式红外传感器 + 停止模式
	int main()
	{
		CountSensor_Init();
		OLED_Init();
		OLED_ShowString(1, 1, "Count:");
		while(1)
		{
			OLED_ShowNum(1, 7, CountSensor_Get(), 5);
			
			OLED_ShowString(2, 1, "Running");
			Delay_ms(100);
			OLED_ShowString(2, 1, "       ");
			Delay_ms(100);
			
			//进入停止模式
			RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
			PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);
			//进入停止模式后会使用HSI时钟,需要重新使用时钟
			SystemInit();
		}
		return 0;
	}

13.5.待机模式

            //进入待机模式,程序将从头开始执行,后续代码不在执行,这也是不用再选择开启主频的原因

	//对射式红外传感器 + 待机模式
	int main()
	{
		MyRTC_Init();
		
		//开启时钟
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
		
		OLED_Init();
		OLED_ShowString(1, 1, "CNT:");
		OLED_ShowString(2, 1, "ALR:");
		OLED_ShowString(3, 1, "ALRF:");
		
		uint32_t alarm = RTC_GetCounter() + 10;
		RTC_SetAlarm(alarm );//闹钟设置为10s后
		OLED_ShowNum(2, 6, alarm , 10);
		
		//使用wakeup唤醒
		PWR_WakeUpPinCmd(ENABLE);
		while(1)
		{
			OLED_ShowNum(1, 6, RTC_GetCounter(), 10);
			OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);
			
			OLED_ShowString(4, 1, "Running");
			Delay_ms(100);
			OLED_ShowString(4, 1, "       ");
			Delay_ms(100);
			
			//进入待机模式,程序将从头开始执行,后续代码不在执行,这也是不用再选择开启主频的原因
			PWR_EnterSTANDBYMode();
		}
		return 0;
	}

14.看门狗

14.1.基础概念

独立看门狗 

窗口看门狗

WWDG 工作特性

  • 递减计数器 T[6:0] 的值小于 0x40 时, WWDG 产生复位
  • 递减计数器 T[6:0] 在窗口 W[6:0] 外被重新装载时, WWDG 产生 复位
  • 递减计数器 T[6:0] 等于 0x40 时可以产生早期唤醒中断( EWI ), 用于重装载计数器以避免 WWDG 复位:作用:复位前保护数据,关闭一些危险元器件
  • 定期写入 WWDG_CR 寄存器(喂狗)以避免 WWDG 复位

独立看门狗和窗口看门狗的区别

14.2.代码实现

不用开启时钟的原因 

	//独立看门狗
	int main()
	{
		
		OLED_Init();
		Key_Init();
		OLED_ShowString(1, 1, "IWDG test:");
		
		if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET)
		{
			OLED_ShowString(2, 1, "IWDG RESET");
			Delay_ms(500);
			OLED_ShowString(2, 1, "          ");
			Delay_ms(100);
			RCC_ClearFlag();
		}
		else
		{
			OLED_ShowString(3, 1, "ELSE RESET");
			Delay_ms(500);
			OLED_ShowString(3, 1, "          ");
			Delay_ms(100);
		}
		IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//取消写保护
		IWDG_SetPrescaler(IWDG_Prescaler_16);
		IWDG_SetReload(2500 - 1);
		IWDG_ReloadCounter();//启动前先喂一次狗,第一次就是完整的时间
		IWDG_Enable();//启动看门狗会改变键寄存器,进入写保护
		while(1)
		{
			GetKeyNum();//函数内有一个等放手的while函数,会导致堵塞
			
			IWDG_ReloadCounter();
			
			OLED_ShowString(4, 1, "FEED");
			Delay_ms(600);
			OLED_ShowString(4, 1, "    ");
			Delay_ms(200);
		}
		return 0;
	}

窗口看门狗

	//独立看门狗
	int main()
	{
		
		OLED_Init();
		Key_Init();
		OLED_ShowString(1, 1, "WWDG test:");
		
		if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET)
		{
			OLED_ShowString(2, 1, "WWDG RESET");
			Delay_ms(500);
			OLED_ShowString(2, 1, "          ");
			Delay_ms(100);
			RCC_ClearFlag();
		}
		else
		{
			OLED_ShowString(3, 1, "ELSE RESET");
			Delay_ms(500);
			OLED_ShowString(3, 1, "          ");
			Delay_ms(100);
		}
		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);
		WWDG_SetPrescaler(WWDG_Prescaler_8);
		WWDG_SetWindowValue(0x40 | 21);
		WWDG_Enable(0x40 | 54);//启动看门狗
		while(1)
		{
			GetKeyNum();//函数内有一个等放手的while函数,会导致堵塞

			OLED_ShowString(4, 1, "FEED");
			Delay_ms(20);
			OLED_ShowString(4, 1, "    ");
			Delay_ms(20);
			
			WWDG_SetCounter(0x40 | 54);//喂狗
		}
		return 0;
	}

15. flash闪存

  • 使用flash闪存存储数据时会占用闪存,这是代码不在执行,因为程序存储器(闪存的一部分)存储着可执行程序

15.2.代码实现使用闪存读写

uint8_t KeyNum;					//定义用于接收按键键码的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	Key_Init();					//按键初始化
	Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");
	
	while (1)
	{
		KeyNum = GetKeyNum();		//获取按键键码
		
		if (KeyNum == 1)			//按键1按下
		{
			Store_Data[1] ++;		//变换测试数据
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失
		}
		
		if (KeyNum == 2)			//按键2按下
		{
			Store_Clear();			//将Store_Data的数据全部清0
		}
		
		OLED_ShowHexNum(1, 6, Store_Data[0], 4);	//显示Store_Data的第一位标志位
		OLED_ShowHexNum(3, 1, Store_Data[1], 4);	//显示Store_Data的有效存储数据
		OLED_ShowHexNum(3, 6, Store_Data[2], 4);
		OLED_ShowHexNum(4, 1, Store_Data[3], 4);
		OLED_ShowHexNum(4, 6, Store_Data[4], 4);
	}
}

 

#include "stm32f10x.h"                  // Device header

uint32_t MyFLASH_ReadWord(uint32_t Address)
{
	return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
	return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

uint8_t MyFLASH_ReadByte(uint32_t Address)
{
	return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();					//解锁
	FLASH_EraseAllPages();			//全擦除
	FLASH_Lock();					//加锁
}

void MyFLASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();					//解锁
	FLASH_ErasePage(PageAddress);	//页擦除
	FLASH_Lock();					//加锁
}

void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
	FLASH_Unlock();							//解锁
	FLASH_ProgramWord(Address, Data);		//编程字
	FLASH_Lock();							//加锁
}


void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
	FLASH_Unlock();							//解锁
	FLASH_ProgramHalfWord(Address, Data);	//编程半字
	FLASH_Lock();							//加锁
}
#ifndef __MYFLASH_H
#define __MYFLASH_H

uint32_t MyFLASH_ReadWord(uint32_t Address);
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
uint8_t MyFLASH_ReadByte(uint32_t Address);

void MyFLASH_EraseAllPages(void);
void MyFLASH_ErasePage(uint32_t PageAddress);

void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

#endif
#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS		0x0800FC00		//存储的起始地址
#define STORE_COUNT				512				//存储数据的个数

uint16_t Store_Data[STORE_COUNT];				//定义SRAM数组

void Store_Init(void)
{
	/*判断是不是第一次使用*/
	if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)	//读取第一个半字的标志位,if成立,则执行第一次使用的初始化
	{
		MyFLASH_ErasePage(STORE_START_ADDRESS);					//擦除指定页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);	//在第一个半字写入自己规定的标志位,用于判断是不是第一次使用
		for (uint16_t i = 1; i < STORE_COUNT; i ++)				//循环STORE_COUNT次,除了第一个标志位
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);		//除了标志位的有效数据全部清0
		}
	}
	
	/*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/
	for (uint16_t i = 0; i < STORE_COUNT; i ++)					//循环STORE_COUNT次,包括第一个标志位
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);		//将闪存的数据加载回SRAM数组
	}
}

void Store_Save(void)
{
	MyFLASH_ErasePage(STORE_START_ADDRESS);				//擦除指定页
	for (uint16_t i = 0; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,包括第一个标志位
	{
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);	//将SRAM数组的数据备份保存到闪存
	}
}

void Store_Clear(void)
{
	for (uint16_t i = 1; i < STORE_COUNT; i ++)			//循环STORE_COUNT次,除了第一个标志位
	{
		Store_Data[i] = 0x0000;							//SRAM数组有效数据清0
	}
	Store_Save();										//保存数据到闪存
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2393127.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[网页五子棋][用户模块]客户端开发(登录功能和注册功能)

文章目录 客户端开发登录功能htmlcsscommon.csslogin.css jQuery引入 jquery 运行程序注册功能 客户端开发 登录功能 html <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport&…

MQTT协议,EMQX部署,MQTTX安装学习

一、MQTT概述 1.什么是MQTT MQTT是一种基于“发布订阅“”模式的消息传输协议。 消息&#xff1a;设备和设备之间传输的数据&#xff0c;或者服务和服务之间要传输的数据。 协议&#xff1a;传输数据时所遵循的规范。 2.常见的通讯模式 &#xff08;1&#xff09;客户端-服…

60天python训练计划----day40

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中展平操作&#xff1a;除第一个维度batchsize外全部展平dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropout 一.单通道图…

干泵,干式螺杆真空泵

干式真空泵&#xff1a; 无油干式机械真空泵&#xff08;又简称干式机械泵&#xff09;是指泵能从大气压力下开始抽气&#xff0c;又能将被抽气体直接排到大气中去&#xff0c;泵腔内无油或其他工作介质&#xff0c;而且泵的极限压力与油封式真空泵同等量级或者接近的机械真空泵…

Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(五):语音合成输出与交互增强

Tailwind CSS 实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;一&#xff09; Tailwind CSS 实战&#xff0c;基于Kooboo构建AI对话框页面&#xff08;二&#xff09;&#xff1a;实现交互功能 Tailwind CSS 实战&#xff0c;基于 Kooboo 构建 AI 对话框页面&#x…

职业本科院校无人机专业人才培养解决方案

2023年的中央经济工作会议强调了以科技创新推动现代化产业体系构建的重要性&#xff0c;并提出发展生物制造、商业航天、低空经济等战略性新兴产业。低空经济&#xff0c;依托民用无人机等低空飞行器&#xff0c;在多场景低空飞行活动的牵引下&#xff0c;正逐步形成一个辐射广…

软件评测机构如何保障质量?检测资质、技术实力缺一不可

软件评测机构在保障软件质量上起着关键作用&#xff0c;对软件行业的健康发展极为关键。它们采用专业的技术手段和严格的评估流程&#xff0c;对软件的运行效果、功能等多方面进行细致的审查&#xff0c;为开发者和使用者提供了客观、公正的参考依据。 检测资质正规软件评测机…

Linux多线程(六)之线程控制4【线程ID及进程地址空间布局】

文章目录 线程ID及进程地址空间布局线程局部存储 线程ID及进程地址空间布局 pthread_ create函数会产生一个线程ID&#xff0c;存放在第一个参数指向的地址中。 该线程ID和前面说的线程ID不是一回事。 前面讲的线程ID属于进程调度的范畴。 ​ 因为线程是轻量级进程&#xff…

1.什么是node.js、npm、vue

一、Node.js 是什么&#xff1f; &#x1f63a; 定义&#xff1a; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境&#xff0c;让你可以在浏览器之外运行 JavaScript 代码&#xff0c;主要用于服务端开发。 &#x1f63a;从计算机底层说&#xff1a;什么是“运…

Xamarin入门笔记(Xamarin已经被MAUI取代)

初级代码游戏的专栏介绍与文章目录-CSDN博客 Xamarin入门 概述 环境 Android开发环境比较简单&#xff0c;自带模拟器&#xff0c;实体机打开开发者模式即可。 iOS开发环境比较复杂&#xff0c;必须搭配Mac电脑&#xff0c;Windows连接Mac开发可能有问题&#xff08;比如发…

排查Oracle文件打开数过多

Oracle数据库在运行过程中&#xff0c;会打开大量的文件以执行其操作&#xff0c;包括数据文件、控制文件、日志文件等。如果Oracle用户打开的文件数过多&#xff0c;可能会引起系统性能下降。下面将深入分析Oracle用户文件打开数的优化策略&#xff0c;以帮助数据库管理员&…

应用层协议http(无代码版)

目录 认识URL urlencode 和 urldecode HTTP 协议请求与响应格式 HTTP 的请求方法 GET 方法 POST 方法 HTTP 的状态码 HTTP 常见 Header Location 关于 connection 报头 HTTP版本 远程连接服务器工具 setsockopt 我们来学习应用层协议http。 虽然我们说, 应用层协…

8.5 Q1|广州医科大学CHARLS发文 甘油三酯葡萄糖指数累积变化与 0-3期心血管-肾脏-代谢综合征人群中风发生率的相关性

1.第一段-文章基本信息 文章题目&#xff1a;Association between cumulative changes of the triglyceride glucose index and incidence of stroke in a population with cardiovascular-kidney-metabolic syndrome stage 0-3: a nationwide prospective cohort study 中文标…

无人机停机坪运行技术分析!

一、运行方式 1. 自动折叠与展开 部分停机坪采用二次折叠设计&#xff0c;通过传动组件实现自动折叠&#xff0c;缩小体积便于运输&#xff1b;展开后最大化停机面积&#xff0c;适应不同任务需求。例如&#xff0c;珠海双捷科技的专利通过两次折叠使停机坪体积最小化&#x…

【Java Web】速通HTML

参考笔记: JavaWeb 速通HTML_java html页面-CSDN博客 目录 一、前言 1.网页组成 1 结构 2 表现 3 行为 2.HTML入门 1 基本介绍 2 基本结构 3. HTML标签 1 基本说明 2 注意事项 4. HTML概念名词解释 二、HTML常用标签汇总 + 案例演示 1. 字体标签 font (1)定义 (2)案例 2…

在线制作幼教早教行业自适应网站教程

你想知道怎么做自适应网站吗&#xff1f;今天就来教你在线用模板做个幼教早教行业的网站哦。 首先得了解啥是自适应网站。简单说呢&#xff0c;自适应网站就是能自动匹配不同终端设备的网站&#xff0c;像手机、平板、电脑等。那如何做幼早教自适应网站呢&#xff1f; 在乔拓云…

Apptrace:APP安全加速解决方案

2021 年&#xff0c;某知名电商平台在 “618” 大促期间遭遇 DDoS 攻击&#xff0c;支付系统瘫痪近 2 小时&#xff1b;2022 年&#xff0c;一款热门手游在新版本上线时因 CC 攻击导致服务器崩溃。观察发现&#xff0c;电商大促、暑期流量高峰和年末结算期等关键商业周期&#…

Web攻防-SQL注入增删改查HTTP头UAXFFRefererCookie无回显报错

知识点&#xff1a; 1、Web攻防-SQL注入-操作方法&增删改查 2、Web攻防-SQL注入-HTTP头&UA&Cookie 3、Web攻防-SQL注入-HTTP头&XFF&Referer 案例说明&#xff1a; 在应用中&#xff0c;存在增删改查数据的操作&#xff0c;其中SQL语句结构不一导致注入语句…

GoldenDB管理节点zk部署

目录 1、准备阶段 1.1、部署规划 1.2、硬件准备 1.3、软件准备 1.4、网络端口开通 1.5、环境清理 2、实施阶段 2.1、操作系统配置 2.1.1、主机名修改 2.1.2、修改hosts文件 2.1.3、禁用防火墙 2.1.4、禁用selinux 2.1.5、禁用透明大页 2.1.6、资源限制调整 2.1.…

mac mini m4命令行管理员密码设置

附上系统版本图 初次使用命令行管理员&#xff0c;让输入密码&#xff0c;无论是输入登录密码还是账号密码&#xff0c;都是错的&#xff0c;百思不得其解&#xff0c;去网上搜说就是登录密码啊 直到后来看到了苹果官方的文档 https://support.apple.com/zh-cn/102367 https…