STM32基础入门学习笔记:内部高级功能应用

文章目录:

一:低功耗模式

1.睡眠模式测试程序

NVIC.h

NVIC.c

key.h

key.c

main.c

2.停机模式测试程序

main.c

3.待机模式测试程序

main.c

二:看门狗

1.独立看门狗测试程序

iwdg.h

iwdg.c

main.c

2.窗口看门狗测试程序

wwdg.h

wwdg.c

main.c

三:TIM定时器

tim.h

tim.c

main.c

四:CRC循环冗余校验计算单元与芯片ID

1.CRC功能测试程序

main.c

2.芯片ID读取程序

main.c

五:还需要补充的知识


这些是单片机的辅助功能

一:低功耗模式

单片机内部功率是各功能部分功率的总和

低功耗模式是通过关掉部分内部功能达到省电

STM32F103单片机共有3种低功耗模式

不同模式会对系统正常工作有一定影响,需要按实际情况选择

低功耗模式只针对单片机内部功能,外接电路产生的功耗不在其内



单片机最小系统电路功耗,不精确测量值
    √正常模式:10mA
    √睡眠模式:2mA
    √停机模式:20uA
    √待机模式:2uA


睡眠模式
    在ARM内核无事可做的时候,可以进入睡眠模式
    例如:电脑的CPU空闲状态就是单片机睡眠模式

    睡眠模式的应用不多,因只关闭ARM内核,节能有限,很少在非操作系统程序(裸机)中使用
    在嵌入式操作系统中,会采用睡眠模式

    优点:对系统影响最小
    缺点:节能效果最差


停机模式
    因SRAM内容不消失,程序不复位,可在唤醒后继续运行
    节能效果与待机模式近似,却有着更多优势

    主要用于电池供电的设备上,提高电池寿命
    在电池供电的产品中必须使用,在外部供电的产品中没必要使用

    优点:节能效果好,程序不会复位
    缺点:恢复时间较长



待机模式
    由于SRAM内容消失,唤醒后程序必须复位,从头开始运行

    因为待机和停机之间的功耗差别是uA级的,几乎没有差别,所以开发者大多使用停机模式,待机模式极少使用
    在一些偶尔需要工作的场合,且工作量不大、不复杂的情况下,待机模式可以保证最低的功耗
    比如应用在室外温度测量产品上,每1小时测量一次。可用RTC闹钟唤醒,测量完再待机。、

    优点:最节能
    缺点:程序会复位,只有少数条件可唤醒

1.睡眠模式测试程序

NVIC.h

#ifndef __NVIC_H
#define __NVIC_H	 
#include "sys.h"


extern u8 INT_MARK;//中断标志位


void KEY_INT_INIT (void);

#endif

NVIC.c

#include "NVIC.h"

u8 INT_MARK;//中断标志位

void KEY_INT_INIT (void){	 //按键中断初始化
	NVIC_InitTypeDef  NVIC_InitStruct;	//定义结构体变量
	EXTI_InitTypeDef  EXTI_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //启动GPIO时钟 (需要与复用时钟一同启动)     
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);//配置端口中断需要启用复用时钟

	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);  //定义 GPIO  中断
	
	EXTI_InitStruct.EXTI_Line=EXTI_Line0;  //定义中断线
	EXTI_InitStruct.EXTI_LineCmd=ENABLE;              //中断使能
	EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;     //中断模式为 中断
	EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling;   //下降沿触发
	
	EXTI_Init(& EXTI_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel=EXTI0_IRQn;   //中断线     
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;  //使能中断
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;  //抢占优先级 2
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;     //子优先级  2
	NVIC_Init(& NVIC_InitStruct);

}

void  EXTI0_IRQHandler(void){
	if(EXTI_GetITStatus(EXTI_Line0)!=RESET){//判断某个线上的中断是否发生 
		INT_MARK=1;//标志位置1,表示有按键中断
		EXTI_ClearITPendingBit(EXTI_Line0);   //清除 LINE 上的中断标志位
	}     
}

key.h

#ifndef __KEY_H
#define __KEY_H	 
#include "sys.h"

//#define KEY1 PAin(0)// PA0
//#define KEY2 PAin(1)// PA1

#define KEYPORT	GPIOA	//定义IO接口组
#define KEY1	GPIO_Pin_0	//定义IO接口
#define KEY2	GPIO_Pin_1	//定义IO接口


void KEY_Init(void);//初始化

		 				    
#endif

key.c

#include "key.h"

void KEY_Init(void){ //微动开关的接口初始化
	GPIO_InitTypeDef  GPIO_InitStructure; //定义GPIO的初始化枚举结构	
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);       
    GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2; //选择端口号(0~15或all)                        
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //选择IO接口工作方式 //上拉电阻       
//    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    
	GPIO_Init(KEYPORT,&GPIO_InitStructure);			
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "NVIC.h"    //中断向量控制器


int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   SLEEP TEST   "); //显示字符串

	INT_MARK=0;//标志位清0
	NVIC_Configuration();//设置中断优先级
	KEY_INT_INIT();//按键中断初始化(PA0是按键中断输入)

	NVIC_SystemLPConfig(NVIC_LP_SEVONPEND,DISABLE);	//SEVONPEND: 0:只有使能的中断或事件才能唤醒内核。1:任何中断和事件都可以唤醒内核。(0=DISABLE,1=ENABLE) 
	NVIC_SystemLPConfig(NVIC_LP_SLEEPDEEP,DISABLE);	//SLEEPDEEP: 0:低功耗模式为睡眠模式。1:进入低功耗时为深度睡眠模式。
    NVIC_SystemLPConfig(NVIC_LP_SLEEPONEXIT,DISABLE); //SLEEPONEXIT: 0: 被唤醒进入线程模式后不再进入睡眠模式。1:被唤醒后执行完相应的中断处理函数后进入睡眠模式。

	while(1){

		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED控制
		OLED_DISPLAY_8x16_BUFFER(4,"  CPU SLEEP!    "); //显示字符串
		delay_ms(500); //

		__WFI(); //进入睡眠模式,等待中断唤醒
//		__WFE(); //进入睡眠模式,等待事件唤醒

		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED控制
		OLED_DISPLAY_8x16_BUFFER(4,"  CPU WAKE UP!  "); //显示字符串
		delay_ms(500); //
	}

}

2.停机模式测试程序

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "NVIC.h"


int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0,"   STOP  TEST   "); //显示字符串

	INT_MARK=0;//标志位清0
	NVIC_Configuration();//设置中断优先级
	KEY_INT_INIT();//按键中断初始化(PA0是按键中断输入)
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);  //使能电源PWR时钟

	while(1){
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED控制
		OLED_DISPLAY_8x16_BUFFER(4,"  CPU STOP!     "); //显示字符串
		delay_ms(500); //

		PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);//进入停机模式

		RCC_Configuration(); //系统时钟初始化(停机唤醒后会改用HSI时钟,需要重新对时钟初始化) 

		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED控制
		OLED_DISPLAY_8x16_BUFFER(4,"  CPU WAKE UP!  "); //显示字符串
		delay_ms(500); //
	}

}

3.待机模式测试程序

对开发板跳线进行设置

触摸按键将最上方的PA0跳线断开

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"


int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化
	OLED_DISPLAY_8x16_BUFFER(0," STANDBY TEST   "); //显示字符串

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);  //使能电源PWR时钟
	PWR_WakeUpPinCmd(ENABLE);//WKUP唤醒功能开启(待机时WKUP脚PA0为模拟输入)

	GPIO_WriteBit(LEDPORT,LED1,(BitAction)(0)); //LED控制
	OLED_DISPLAY_8x16_BUFFER(4,"  CPU RESET!    "); //显示字符串
	delay_ms(500); //

	GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1)); //LED控制
	OLED_DISPLAY_8x16_BUFFER(4,"   STANDBY!     "); //显示字符串
	delay_ms(500); //

	PWR_EnterSTANDBYMode();//进入待机模式

	//因为待机唤醒后程序从头运行,所以不需要加while(1)的主循环体。
}

二:看门狗

是单片机系统功能的一个辅助功能:帮助单片机自我检查、 监控单片机程序是否正常工作

看门狗定时器(WDT,Watch Dog Timer)是单片机的一个组成部分
    它实际上是一个计数器,一般给看门狗计数值,程序开始运行后看门狗开始倒计数
    如果程序运行正常,过一段时间CPU应发出指令让看门狗复位,重新开始倒计数
    如果看门狗减到0就认为程序没有正常工作,强制整个系统复位

    看门狗是一个计数器
    启动后开始倒计时
    每过一段时间CPU要重新写入计数值(喂狗)
    CPU能重写计数值,表示程序运行正常
    如果程序运行出错或死机,则不能重写计数值
    当计数值减到O时,看门狗会让整个单片机复位



看门狗的作用
    看门狗的主要目的是监控单片机程序
    如果程序不断喂狗,就证明单片机工作正常
    如果程序没有喂狗,就说明单片机出了问题
    看门狗不能检查问题的原因,只能通过复位单片机,让程序重新开始运行



类型:独立看门狗、窗口看门狗
独立看门狗可在计数到O前随时喂狗

用于监控程序是否正常运行

窗口看门狗必须在规定的时间范围内喂狗

作用是监控单片机运行时效是否精确

1.独立看门狗测试程序

新建文件夹

Basic文件夹------>iwdg文件夹------>iwdg.c  iwdg.h

iwdg.h

#ifndef __IWDG_H
#define __IWDG_H	 
#include "sys.h"

//看门狗定时时间计算公式:Tout=(预分频值*重装载值)/40 (单位:ms)
//当前pre为64,rlr为625,计算得到Tout时间为1秒(大概值)。

#define pre		IWDG_Prescaler_64 //分频值范围:4,8,16,32,64,128,256
#define rlr		625 //重装载值范围:0~0xFFF(4095)


void IWDG_Init(void);
void IWDG_Feed(void);
		 				    
#endif

iwdg.c

#include "iwdg.h"


void IWDG_Init(void){ //初始化独立看门狗
    IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); //使能对寄存器IWDG_PR和IWDG_RLR的写操作
    IWDG_SetPrescaler(pre); //设置IWDG预分频值
    IWDG_SetReload(rlr); //设置IWDG重装载值
    IWDG_ReloadCounter(); //按照IWDG重装载寄存器的值重装载IWDG计数器
    IWDG_Enable(); //使能IWDG
}

void IWDG_Feed(void){ //喂狗程序
    IWDG_ReloadCounter();//固件库的喂狗函数
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "iwdg.h"

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化---------------"
	OLED_DISPLAY_8x16_BUFFER(0,"   IWDG TEST    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(4,"    RESET!      "); //显示字符串
	delay_ms(800); //
	OLED_DISPLAY_8x16_BUFFER(4,"                "); //显示字符串

	IWDG_Init(); //初始化并启动独立看门狗

	while(1){

		IWDG_Feed(); //喂狗

		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){
			delay_s(2);	//延时2秒,使程序不能喂狗而导致复制
		}
	}
}

2.窗口看门狗测试程序

新建文件夹

Basic文件夹------>wwdg文件夹------>wwdg.c  wwdg.h

wwdg.h

#ifndef __WWDG_H
#define __WWDG_H	 
#include "sys.h"

//窗口看门狗定时时间计算公式:
//上窗口超时时间(单位us) = 4096*预分频值*(计数器初始值-窗口值)/APB1时钟频率(单位MHz)
//下窗口超时时间(单位us) = 4096*预分频值*(计数器初始值-0x40)/APB1时钟频率(单位MHz)

#define WWDG_CNT	0x7F //计数器初始值,范围:0x40~0x7F
#define wr		0x50 //窗口值(上窗口边界),范围:0x40~0x7F
#define fprer	WWDG_Prescaler_8 //预分频值,取值:1,2,4,8

//如上三个值是:0x7f,0x50,8时,上窗口48MS,下窗口64MS。

void WWDG_Init(void);
void WWDG_NVIC_Init(void);
void WWDG_Feed(void);
		 				    
#endif

wwdg.c

#include "wwdg.h"


void WWDG_Init(void){ //初始化窗口看门狗
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); // WWDG 时钟使能
	WWDG_SetPrescaler(fprer); //设置 IWDG 预分频值
	WWDG_SetWindowValue(wr); //设置窗口值
	WWDG_Enable(WWDG_CNT); //使能看门狗,设置 counter
	WWDG_ClearFlag(); //清除提前唤醒中断标志位
	WWDG_NVIC_Init(); //初始化窗口看门狗 NVIC
	WWDG_EnableIT(); //开启窗口看门狗中断
}

void WWDG_NVIC_Init(void){ //窗口看门狗中断服务程序(被WWDG_Init调用)
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn; //WWDG 中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占 2 子优先级 3 组 2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //抢占 2,子优先级 3,组 2
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure); //NVIC 初始化
}

void WWDG_Feed(void){ //窗口喂狗程序
    WWDG_SetCounter(WWDG_CNT); //固件库的喂狗函数
}

void WWDG_IRQHandler(void){	//窗口看门狗中断处理程序
	WWDG_ClearFlag(); //清除提前唤醒中断标志位

	//此处加入在复位前需要处理的工作或保存数据
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "wwdg.h"

int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化---------------"
	OLED_DISPLAY_8x16_BUFFER(0,"   WWDG TEST    "); //显示字符串
	OLED_DISPLAY_8x16_BUFFER(4,"    RESET!      "); //显示字符串
	delay_ms(800); //
	OLED_DISPLAY_8x16_BUFFER(4,"                "); //显示字符串

	WWDG_Init(); //初始化并启动独立看门狗

	while(1){
		delay_ms(54); //用延时找到喂狗的窗口时间    避开计数初始值到上窗口边界这段时间
		WWDG_Feed(); //喂狗

		if(!GPIO_ReadInputDataBit(KEYPORT,KEY1)){
			delay_s(2);	//延时2秒,使程序不能喂狗而导致复制
		}
	}
}

三:TIM定时器

定时器的3种功能
    捕获器:测量波形的频率和宽度
    比较器:分为模拟比较器和输出比较器
        模拟比较器:比较两组输入电压的大小(STM32F103无此功能)
        输出比较器:产生可调频率和可调占空比的脉冲波形
    PWM:脉宽调制器,产生固定频率但占空比可调的脉冲波形



普通定时器
    定时器可以用于独立时间计时功能,原理和嘀嗒定时器、看门狗基本相同
    定时时间到时,可等待CPU检查标志位(查寻方式),或产生"定时器中断"
    ---般都是让定时器产生中断



捕获器
    捕获什么?    输入接口的电平变化(上升沿或下降沿)
        上升沿:从低电平到高电平
        下降沿:从高电平到低电平

    有什么用?    可测量脉冲的宽度,或者测量脉冲频率
        宽度
            T1是上沿捕获的定时器值
            T2是下沿捕获的定时器值
            T2-T1=高电平宽度值
        频率
            T1是第1次上沿捕获值
            T2是第2次上沿捕获值
            T2-T1=一个周期时间值(频率)

    工作过程!    当接口产生上升沿或下降沿时,将当前定时器值保存




输出比较器
    可输出脉冲,可调占空比和频率
        每一个周期的长度都可以不同
        每一个周期内的占空比都可以不同

    PWM只能调占空比(也是可以通过程序调频率,但不方便随时调)
    输出比较器可随时调占空比和频率

    输出比较器主要用于步进电机、伺服电机的控制

定时器中断测试程序

新建文件夹

Basic文件夹------>tim文件夹------>tim.c  tim.h

tim.h

#ifndef  __PWM_H
#define  __PWM_H
#include "sys.h"

void TIM3_Init(u16 arr,u16 psc);
void TIM3_NVIC_Init (void);


#endif

tim.c

#include "led.h" //因在中断处理函数中用到LED驱动

#include "tim.h"

//定时器时间计算公式Tout = ((重装载值+1)*(预分频系数+1))/时钟频率;
//例如:1秒定时,重装载值=9999,预分频系数=7199

void TIM3_Init(u16 arr,u16 psc){  //TIM3 初始化 arr重装载值 psc预分频系数
    TIM_TimeBaseInitTypeDef     TIM_TimeBaseInitStrue;
    
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3
    TIM3_NVIC_Init (); //开启TIM3中断向量
	      
    TIM_TimeBaseInitStrue.TIM_Period=arr; //设置自动重装载值
    TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //预分频系数
    TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //计数器向上溢出
    TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); //TIM3初始化设置
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);//使能TIM3中断    
    TIM_Cmd(TIM3,ENABLE); //使能TIM3
}

void TIM3_NVIC_Init (void){ //开启TIM3中断向量
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x3;	//设置抢占和子优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

void TIM3_IRQHandler(void){ //TIM3中断处理函数
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){	//判断是否是TIM3中断
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

        //此处写入用户自己的处理程序
		GPIO_WriteBit(LEDPORT,LED1,(BitAction)(1-GPIO_ReadOutputDataBit(LEDPORT,LED1))); //取反LED1
    }
}

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "tim.h"


int main (void){//主程序
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化---------------"
	OLED_DISPLAY_8x16_BUFFER(0,"   TIM TEST     "); //显示字符串

	TIM3_Init(9999,7199);//定时器初始化,定时1秒(9999,7199)

	while(1){

	//写入用户的程序
	//LED1闪烁程序在TIM3的中断处理函数中执行


	}
}

四:CRC循环冗余校验计算单元与芯片ID

1.CRC功能测试程序

CRC(循环冗余校验)计算单元
    使用一个固定的多项式发生器,从一个32位的数据字产生一个CRC码在众多的应用中,基于CRC的技术被用于验证数据传输或存储的一致性
    在EN/IEC 60335-1标准的范围内,它提供了一种检测闪存存储器错误的手段,CRC计算单元可以用于实时地计算软件的签名,并与在链接和生成该软件时产生的签名对比。
        √CRC是用于数据正确性校验的
        √由一个32位的数据字产生√可应用在FALSH检测
        √可用于软件签名及对比
    特点:写入和读出都是同一个寄存器,但内容却不同

main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"


int main (void){//主程序
	u32 a,b;
	u8 c;
	u32 y[3]={0x87654321,0x98765432,0x09876543};
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化---------------"
	OLED_DISPLAY_8x16_BUFFER(0,"   CRC TEST     "); //显示字符串

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);//开启CRC时钟

	while(1){
		CRC_ResetDR();//复位CRC,需要清0重新计算时先复位
		CRC_CalcCRC(0x12345678);//CRC计算一个32位数据。参数:32位数据。返回值:32位计算结果
		CRC_CalcCRC(0x23456789);//CRC计算一个32位数据。参数:32位数据。返回值:32位计算结果
		a = CRC_CalcCRC(0x34567890);//CRC计算一个32位数据。参数:32位数据。返回值:32位计算结果

		CRC_ResetDR();//复位CRC,需要清0重新计算时先复位
		b = CRC_CalcBlockCRC(y,3);//CRC计算一个32位数组。参数:32位数组名,数组长度。返回值:32位计算结果

		CRC_SetIDRegister(0x5a);//向独立寄存器CRC_IDR写数据。参数:8位数据。
		c = CRC_GetIDRegister();//从独立寄存器CRC_IDR读数据。返回值:8位数据。

		//此时,a存放的是3个独立数的CRC结果。(32位)
		//b存放的是数组y中3个数据CRC计算结果。(32位)
		//c存放的是我们写入的独立寄存器数据0x5a。(8位)
	}
}

//	以下是CRC固件库函数,可在主程序中直接调用  //

//	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);//开启CRC时钟,主程序开始时调用
//	CRC_ResetDR();//复位CRC,需要清0重新计算时先复位
//	uint32_t CRC_CalcCRC(uint32_t Data);//CRC计算一个32位数据。参数:32位数据。返回值:32位计算结果
//	uint32_t CRC_CalcBlockCRC(uint32_t pBuffer[], uint32_t BufferLength);//CRC计算一个32位数组。参数:32位数组名,数组长度。返回值:32位计算结果
//	uint32_t CRC_GetCRC(void);//从CRC中读出计算结果。返回值:32位计算结果。

//	void CRC_SetIDRegister(uint8_t IDValue);//向独立寄存器CRC_IDR写数据。参数:8位数据。
//	uint8_t CRC_GetIDRegister(void);//从独立寄存器CRC_IDR读数据。返回值:8位数据。

stm32f10x_crc. c包含了CRC的固件库函数

2.芯片ID读取程序

芯片ID
    √ 96位ID编码
    √可读出3个32位数据,或8个8位数据
    √可以以字节(8位)为单位读取,也可以以半字(16位)或者全字(32位)读取
    √每个芯片编码是唯一的,出厂时固化,不可修改
    √ 可用于产品序列号
    √用来作为密码,提高安全性    
    √用来保护程序的不可复制


main.c

#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "delay.h"
#include "relay.h"
#include "oled0561.h"
#include "led.h"
#include "key.h"

#include "usart.h"

int main (void){//主程序
	u32 ID[3];
	delay_ms(500); //上电时等待其他器件就绪
	RCC_Configuration(); //系统时钟初始化 
	RELAY_Init();//继电器初始化
	LED_Init();//LED 
	KEY_Init();//KEY

	USART1_Init(115200); //串口初始化(参数是波特率)
	I2C_Configuration();//I2C初始化

	OLED0561_Init(); //OLED初始化---------------"
	OLED_DISPLAY_8x16_BUFFER(0,"  CHIP ID TEST  "); //显示字符串

	ID[0] = *(__IO u32 *)(0X1FFFF7E8); //读出3个32位ID 高字节
	ID[1] = *(__IO u32 *)(0X1FFFF7EC); // 
	ID[2] = *(__IO u32 *)(0X1FFFF7F0); // 低字节

    //08表示后面的数据不足8位就补0显示
	printf("ChipID: %08X %08X %8X \r\n",ID[0],ID[1],ID[2]); //从串口输出16进制ID

	if(ID[0]==0x066EFF34 && ID[1]==0x3437534D && ID[2]==0x43232328){ //检查ID是否匹配
		printf("chipID OK! \r\n"); //匹配
	}else{
		printf("chipID error! \r\n"); //不同
	}

	while(1){

	}
}

五:还需要补充的知识

1.仿真
    仿真接口有JTAG、SW接口,仿真器又有ST-LINK、J-LINK等
    还有纯软件仿真Proteus等


2.HEL库


3.内置USB接口
    USB鼠标、键盘、U盘


4.显示屏
    除OLED之外,还能外扩LCD1602、12864等
    显示屏的类型还有VOG屏、TFT屏等
    每种屏的接口也分好多种


5.定时器的复杂功能
    TIM1高级定时器的使用
    单脉冲模式
    输出比较器的使用
    捕获器的使用
    定时器的DMA设置


6.中断的复杂功能
    中断嵌套应用与优先级问题
    外部中断的端口映射问题


7.单片机内部功能
    WIFI、蓝牙、GPS模块、2.4G模块、彩屏的人机界面、RTOS嵌入式操作系统....
相关推荐
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
龙鸣丿2 小时前
Linux基础学习笔记
linux·笔记·学习
Nu11PointerException4 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
亦枫Leonlew6 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
考试宝6 小时前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物
黑叶白树8 小时前
简单的签到程序 python笔记
笔记·python
@小博的博客8 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
幸运超级加倍~8 小时前
软件设计师-上午题-15 计算机网络(5分)
笔记·计算机网络
南宫生8 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步9 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝