任务:开启时单片机回复已打开;关闭时在电脑显示已关闭;发 送 1 打开;发送 2 关闭。
本次学习是基于STM32的通用定时器结合串口,进行对LED灯闪烁的控制,使得延时函数时带来的误差性,以及消耗大量的CPU的资源,一直在循环里空跑等的浪费单片机资源的现象得到了极大的改善,最主要的是,得到了LED灯在定时器的控制下,可以暂停在当时运行状态下,再在暂停时状态开始跑动的现象。
代码的总体布局:
(这里虽然有两处警告,但是运行结果0错误,0警告,暂时还没找出是什么问题哈)
main.c
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "sys.h"
#include "led.h"
#include "time.h"
uint8_t RxData;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);//设定中断优先级分组0
led_init();
Serial_Init();
while (1)
{
while(Serial_GetRxFlag() == 1)
{ RxData = Serial_GetRxData();
switch(RxData)
{
case 1 : time3_init(7199,9999);//1s产生一次中断,用于控制LED进行闪烁
NVIC_INIT();
Serial_Printf("打开\r\n");
break;
case 2 : TIM_Cmd(TIM3,DISABLE);Serial_Printf("关闭\r\n");
break;
}
}
}
}
在第八行对NVIC进行分组,放在主函数的目的是,让定时器和串口用到的NVIC在同一组,进而设置它们的抢占优先级,和响应优先级进行区分它们的优先级。
(卡脖子的地方在于 对STM32的基本语句的基本功能,基本用法不是很熟悉:例如:想要定时器不工作时,直接关了定时器的时钟:case 2 :TIM_Cmd(TIM3,DISABLE);)
串口模块代码Seriai.h
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;
void Serial_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);//使能APB2外设时钟 打开USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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_InitStruct 中指定的参数初始化外设 USARTx 寄存器
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;//该成员设置了 USART 传输的波特率
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;//在帧结尾传输 1 个停止位
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8 位数据
USART_Init(USART1, &USART_InitStructure);
/****开启RXNE标志位到NVIC的输出****/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/***配置 嵌套向量中断控制器(NVIC)***/
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn ;//USART1 全局中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x1;//响应优先级
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART1, ENABLE);//使能USART串口外设
}
void Serial_SendByte(uint8_t Byte)//发送一位函数
{
USART_SendData(USART1, Byte);//发送数据
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//接收标志位状态
}
void Serial_SendString(char *String)//发送字符串函数
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}
//重定向printf函数到串口:对printf进行重定向的方式来把打印信息printf到串口助手
//fputc是printf的底层头文件
//需要加#include <stdarg.h>
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch);
return ch;
}
void Serial_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}
uint8_t Serial_GetRxFlag(void)//接收标志位函数
{
if(Serial_RxFlag == 1)
{
Serial_RxFlag = 0;
return 1;
}
return 0;
}
uint8_t Serial_GetRxData(void)//接收到的数据
{
return Serial_RxData;
}
void USART1_IRQHandler(void)//串口 1 中断服务程序
{
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)// 接收中断状态标志位
{
Serial_RxData = USART_ReceiveData(USART1);//串口接收数据
Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除中断标志位
}
}
在串口模块中第一个串口初始化函数中分别对单片机的发送引脚和接收引脚进行配置,及串口的基本配置,和对串口NVIC进行配置进而能实现串口的中断。
Serial.h
#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char *String);
void Serial_Printf(char *format, ...);
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
#endif
led.c
#include "stm32f10x.h" // Device header
#include"led.h"
void led_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//通用推挽输出模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
}
led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
#define LED0 PCout(13)
void led_init(void);
#endif
sys.h主要是在这里调用PC13口的LED,当然也可以是其他的接口,比如PA1可以是PAout(1)。
time.c
#include "stm32f10x.h" // Device header
#include "time.h"
#include "led.h"
void time3_init(u16 per,u16 pre)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3的时钟
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//TIM_CKD_DIV1是.h文件中已经定义好的,TIM_CKD_DIV1=0,也就是时钟分频因子为0
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式为向上计数
TIM_TimeBaseStructure.TIM_Period=per;//周期
TIM_TimeBaseStructure.TIM_Prescaler=pre;//分频系数
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//使能TIM3的更新中断
TIM_Cmd(TIM3,ENABLE);//使能TIM3
}
void NVIC_INIT(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;//设定中断向量 本程序为TIM3的中断
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//使能响应中断向量的中断响应
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x1;//配置中断向量的抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x1;//配置中断向量的响应优先级
NVIC_Init(&NVIC_InitStructure);//根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
void TIM3_IRQHandler(void) //TIME3中断服务函数 需要设定中断优先级 即NVIC配置
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)//判断是否发生了更新(溢出)中断
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除溢出中断标志位
}
LED0=!LED0;
}
time.h
#ifndef __TIME_H
#define __TIME_H
#include "sys.h"
void time3_init(u16 per,u16 pre);
void NVIC_INIT(void);
#endif
sys.h在这里的作用主要是为了能够方便的调用IO口,比如LED的接口为PC13
sys.h
//sys.h
#ifndef __SYS_H
#define __SYS_H
#include "stm32f10x.h"
//以下为位带操作代码,若不使用可忽略
#define BITBAND(addr, bitnum) ((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define GPIOA_ODR_Addr (GPIOA_BASE + 12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE + 12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE + 12) //0x4001100C
#define GPIOA_IDR_Addr (GPIOA_BASE + 8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE + 8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE + 8) //0x40011008
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr, n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr, n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr, n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr, n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr, n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr, n) //输入
#endif //__SYS_H
为减少传输过程和显示过程因的进制问题而导致的错误,Keil和串口助手进行如下配置:
补充相关NVIC知识点:
以及在参考手册中学习到的输入输出引脚的配置:
效果展示:
串口控制LED闪烁
(歉言:咳咳,拍视频手抖)
(感悟:忙活半天,发觉STM32的使用和51单片机的使用在思维上紧密联合)
(不会接线的的私聊我哦~)