STM32杂项

STM32杂项

记录单片机在工作中遇到的问题和特殊情况。

1.启动过程

M3/M4/M7内核复位后,做的第一件事:

1.从地址0x0000 0000处取出++堆栈指针MSP的初始值++ ,该值就是栈顶地址

2.从地址0x0000 0004处取出++程序计数器PC的初始值++ ,该值是复位向量

在系统复位后,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。

2.中断

NVIC-管理中断;

EXTI-外部中断控制器;

中断:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行。

中断向量表:

定义一块固定的内存,以4字节对齐,存放各个中断服务函数程序的首地址。

中断向量表定义在++启动文件++ ,当发生中断,CPU会自动执行对应的中断服务函数。

NVIC相关寄存器:

抢占优先级(pre):高优先级可以打断正在执行的低抢占优先级中断。

响应优先级(sub):当抢占优先级相同时,响应优先级高的先执行,但不能相互打断。

抢占和响应都相同的情况下,自然优先级越高的,先执行。

自然优先级:中断向量表中的优先级。

数字越小,表示优先级越高。

分组:

优先级分组 分配结果
0 0位抢占优先级,4位响应优先级
1 1位抢占优先级,3位响应优先级
2 2位抢占优先级,2位响应优先级
3 3位抢占优先级,1位响应优先级
4 4位抢占优先级,0位响应优先级

一个工程中,一般只设置一次中断优先级分组。多次的话,以最后一次为准。

3.GPIO

工作电压范围:2V<VDD<3.6V。

识别电压范围:

低电压:-0.3V<V<1.164V。

高电压:1.833V<V<3.6V。

输出电流:单个IO,最大25MA。

类型:电源引脚,晶振引脚,复位引脚,下载引脚,BOOT引脚,GPIO引脚。

4.Systick

systick:系统滴答定时器,包含在M3/M4/M7内核里面,核心是一个24位的递减计数器。
72mhz经过分频器(CLKSOURCE),每来一个时钟,VAL自动减1,当VAL减到0时,COUNTFLAG变成1(可以判断这位是否为1,来判断是否到达延时时间,退出等待),并且LOAD的值自动赋给VAL重新开始计时。

其中VAL和LOAD的值为2的24次方(0~16777215)。

以下是不同的几种延时函数的写法:

c 复制代码
/*
第一种写法:不需要进行延时初始化,每执行一遍Delay_us()
相当于配置一遍滴答定时器,等待COUNTFLAG变成1。当不在调
用延时函数时停止滴答定时器。
*/
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值 2的24次方 16777216
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
}
c 复制代码
/*第二种写法:需要进行延时的初始化,在初始化的时候配置滴答定时器
过多长时间进行一次中断(通常为1ms),此后不在配置。定义一个系统时间,
在定时器中断里进行加一。需要进行延时处理的时候,在延时函数里定义一
个新的变量,并将系统时间和需要延时的时间相加赋给这个变量,然后等待
系统时间等于这个变量。
*/

vu32 System_Time = 0;//系统时间
void SysTick_Handler(void)
{
	System_Time++;//1ms自加一次
}

void delay_ms(uint32_t time)
{
	uint32_t tick = System_Time + time;
	while(System_Time < tick)  //等待系统时间等于tick
	{}
}

//系统定时器的初始化
//想要1ms来一次中断
//参数:重装载值
void SysTick_Init(uint32_t reload)
{
	if(SysTick_Config(reload))
	{
		while(1);
	}
}
void delay_us(uint32_t time)
{
	while(time--)
		delay_1us();
}

while(1)
{
//延时初始化 1ms
	SysTick_Init(72000); 
}
/*
由于这个延时时间是以ms为单位的,需要us延时时可以加入汇编中
的------NOP()命令进行空等待。
*/
#define delay_1us()	{\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();\
__NOP();__NOP();\
}
/*延迟初始化函数,实际是调用了SysTick_Config()函数,
这个函数为标准库中函数库 */
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
//判断自动重装值是否超过界限。
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            
//设置自动重装值                                                            
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     
//设置滴答定时器中断
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  
//将VAL清零
  SysTick->VAL   = 0;                                        
//配置CTRL 1分频,打开中断,开滴答定时器
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    
  return (0);                                                  
}

5.串口printf

printf函数输出流程:

我们通过需要输出的硬件来重新定义fputc()函数.

c 复制代码
/*
	重定义fputc函数,使printf()函数在打印的过程中,
	也将数据通过串口进行输出
*/
int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数
	return ch;
}

//	其他串口使用printf()
/*
	方法一: 定义一个数组,使用sprintf()函数进行中转一下
		   再用该串口发送字符串的函数进行发送。
*/
char string[100];
sprintf(string,"num=%d\r\n",555);
serial_sendstring(string);

/*
  方法二:封装sprintf()
*/
void Serial_Printf(char *format, ...)
{
	char String[100];				//定义字符数组
	va_list arg;					//定义可变参数列表数据类型的变量arg
	va_start(arg, format);			//从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中
	va_end(arg);					//结束变量arg
	Serial_SendString(String);		//其他串口发送字符数组(字符串)
}

6.独立看门狗

IWDG:独立看门狗

本质:可以产生系统复位信号的计数器。

特点:时钟为低速时钟LSI(40kHZ)由独立的RC振荡器提供(可在停止和待机模式下运行)

看门狗被激活后,当递减计数器计数到0x000时,产生系统复位。

喂狗:在计数器计数到0之前要进行喂狗,防止系统一直被复位。

c 复制代码
	/*IWDG初始化*/
	//独立看门狗的时钟为40khz 低速时钟LSI驱动
void IWDG_init()	
{	
	//独立看门狗写使能
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);	
	//设置预分频为16
	IWDG_SetPrescaler(IWDG_Prescaler_16);
	/设置重装值为2499,独立看门狗的超时时间为1000ms 范围0~4095			
	IWDG_SetReload(2499);	
	//重装计数器,喂狗						
	IWDG_ReloadCounter();			
	//独立看门狗使能				
	IWDG_Enable();									
}

7.不定时跟新...

相关推荐
yutian06062 小时前
Keil MDK下载程序后MCU自动重启设置
单片机·嵌入式硬件·keil
析木不会编程4 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉8 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6779 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普9 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣9 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室10 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费10 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623111 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
liyinuo201711 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范