STM32杂项
- 1.启动过程
- 2.中断
- 3.GPIO
- 4.Systick
- 5.串口printf
- 6.独立看门狗
记录单片机在工作中遇到的问题和特殊情况。
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)。
以下是不同的几种延时函数的写法:
/*
第一种写法:不需要进行延时初始化,每执行一遍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);
}
}
/*第二种写法:需要进行延时的初始化,在初始化的时候配置滴答定时器
过多长时间进行一次中断(通常为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()函数.
/*
重定义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之前要进行喂狗,防止系统一直被复位。
/*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.不定时跟新…