我学习的主要芯片为STM32F407ZGT6
首先看了一些概念的东西,初步了解了STM32
然后新建了工程,开始模仿着写一些简单的程序(库函数方法)
跑马灯实验
- 使能IO口时钟,这几乎是写所有程序的第一步,要注意不同的外设调用的时钟使能函数可能不一样。
- 初始化IO口
- 操作IO口,输出高低电平GPIO_SetBits();→相当于LED0=1→灭;GPIO_ResetBits();→相当于LED0=0→`亮
操作IO口也可以用位带操作,需在led程序的头文件中写以下程序:
#ifndef __BEEP_H
#define __BEEP_H
#include "stm32f4xx.h"
#define BEEP PFout(8)
void BEEP_Init(void);
#endif
蜂鸣器实验
和跑马灯类似,注意LED和蜂鸣器都是输出的工作方式,初始化IO口时,一般设置为推挽输出。使用位带操作时,PFout(9)=0;
按键输入
普通key:上拉输入,一端接IO口,一端接GND(按下时测到低电平,低电平有效)
WK_up:下拉输入,一端接IO口,一端接VCC(按下时测到高电平,高电平有效)
按键输入的步骤为:
使能按键对应IO口的时钟;
初始化IO口,注意按键属于输入的工作方式;
扫描IO口电平;
同时我们应考虑到,连续按还是不连续按,这里用到static和mode。
u8 KEY_Scan(u8 mode)
{
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
{
delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return 1;
else if(KEY1==0)return 2;
else if(KEY2==0)return 3;
else if(WK_UP==1)return 4;
}else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;// 无按键按下
}
mode用于选择支持连续按还是不支持;
static说明该函数不是一个可重入函数,初始化只会被调用一次。
同时,还应知道,按键扫描是有优先级的,在后续学到中断,进一步总结。
呼吸灯
在学习了跑马灯,蜂鸣器,按键之后,写了呼吸灯程序练习。
最初我觉得对于IO口,我们不是给他高电平,就是低电平,所以如果点灯的话,灯也应只有两种状态。但其实我们可以用延时来达到呼吸灯的效果。
这里用到占空比,简单说就是我们控制灯的亮灭,让他亮的时间持续变长,人眼看到的就是他在渐渐变亮,而其实他是不停的亮灭。渐渐变灭也同理,让他变灭的时间持续变长。由于分为两种情况,我们可以用简单的menu菜单。下面程序
#include "stm32f4xx.h"
#include "led.h"
#include "delay.h"
#include "beep.h"
#include "usart.h"
#include "key.h"nt
main(void)
{
int MENU;
int t,i;
LED_Init();
delay_init(168);
MENU=0;
t=1;
while(1)
{
if (MENU==0)
{
for(i = 0; i < 10; i++)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_10 );
delay_us(t);
GPIO_SetBits(GPIOF,GPIO_Pin_10 );
delay_us(501-t);
GPIO_ResetBits(GPIOF,GPIO_Pin_9 );
delay_us(t);
GPIO_SetBits(GPIOF,GPIO_Pin_9 );
delay_us(501-t);
}
t++;
if(t==500)
{
MENU=1;
}
}
if (MENU==1)
{
for(i = 0; i < 10; i++)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_10 );
delay_us(t);
GPIO_SetBits(GPIOF,GPIO_Pin_10 );
delay_us(501-t);
GPIO_ResetBits(GPIOF,GPIO_Pin_9 );
delay_us(t);
GPIO_SetBits(GPIOF,GPIO_Pin_9 );
delay_us(501-t);
}
t--;
if(t==0)
{
MENU=0;
t=1;
}
}
}
}
时钟系统
SYSCLR系统时钟由:HSE OSC高速的外部时钟;HSI RC高速的内部时钟;主PLL时钟得来。
PLL=8MHzN/(M*P)=8MHz336/(8*2)=168MHz
任何一个外设在没有使用之前,都必须使能相应的时钟。
NVIC中断优先级分组
一般只设置一次中断优先级,不随意改变。
步骤:
系统运行后,先设置中断优先级分组
针对每个中断,设置对应的抢占优先级,响应优先级
如需要挂起/解挂,查看中断当前激活状态
1. 高优先级的抢占优先级是可以打断正在进行的低抢占优先级的中断
2. 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级
3. 抢占优先级相同的中断,当两个中断同事发生,响应优先级高的先发生
4. 如果两个中断的抢占优先级,响应优先级都相同,那个中断先发生,就先执行
串口通信
① 串口时钟使能,GPIO 时钟使能
② 设置引脚复用器映射
③ GPIO 端口初始化设置
④ 串口参数初始化
⑤ 初始化 NVIC 并且开启中断
⑥ 使能串口
中断服务函数:
void USART1_IRQHandler(void)
{
u8 Res;
#if SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
}
}
}
}
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
这个函数一般使用在中断服务函数的开头判断中断是否发生。另一个函数是清除某个中断线上
的中断标志位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
这个函数一般应用在中断服务函数结束之前,清除中断标志位。
常用的中断服务函数格式为:
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{ …中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除 LINE 上的中断标志位
}
}
外部中断
1) 使能 IO 口时钟,初始化 IO
2) 开启 SYSCFG 时钟,设置 IO 口与中断线的映射关系。
3) 初始化线上中断,设置触发条件等。
4) 配置中断分组(NVIC ),并使能中断。
5) 编写中断服务函数。
定时器中断
1 )TIM3 时钟使能。
2 )初始化定时器参数 数, 设置 自动重装值 , 分频系数 ,计数方式 等。
3 )设置 TIM3_DIER 允许更新中断
4 )TIM3 中断优先级设置。
5 )允许 TIM3 工作,也就是使能 TIM3 。
6 )编写中断服务函数。
/************更新于2018/1/24*************/
/**************欢迎指正***************/