实验器材
stm32f103c8t6,stlink
实验目的
综合使用实验开发板上的四个led和usart串口,结合前面学习的gpio、定时器、usart等知识,设计综合实验
实验内容
实现功能:上位机通过usart通信控制由定时器中断驱动的led灯切换不同闪烁模式,并实现简单的命令提示功能
应用场景是需要实时手动调整运行参数的环境,通过串口通信可以实时调参,极大避免了每次更新程序参数再烧录的繁琐流程和时间的浪费。
实现代码
cpp
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "misc.h"
#include "stdio.h"
#include "stm32f10x_usart.h"
uint16_t dat = 'h'; // 保存接收到的数据
int led_state = 0; // led状态
void gpio_init(void);
void TIM2_Configuration(void);
void USART1_Config(void);
void LED_POWER1(int k);
void LED_POWER2(int k);
void LED_POWER3(int k);
void LED_POWER(int k);
void INFO();
int main(void)
{
gpio_init(); // GPIO 初始化
USART1_Config(); // 配置 USART1
TIM2_Configuration(); // 配置定时器
// printf("Please Input string ! \r\n"); // 开机后提示输入字符串
for (;;)
;
}
void gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义一个 GPIO 初始化类型结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 开启 AFIO 时钟和 GPIOB\A 的外设时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); // 禁用 JTAG 功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4; // 选择要控制的 GPIOB 引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速率为 50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置引脚模式为通用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure); // 调用库函数,初始化 GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // 选择要控制的 GPIOA 引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 用作串口输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 用作串口输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void TIM2_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 定义一个定时器初始化参数结构体
NVIC_InitTypeDef NVIC_InitStructure; // 定义 NVIC 初始化结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 开启 APB1 的 TIM2 外设时钟
TIM_DeInit(TIM2); // 将外设 TIM2 的寄存器重设为缺省值
TIM_TimeBaseStructure.TIM_Prescaler = (7200 - 1); // 设置预分频系数为(7200-1)
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseStructure.TIM_Period = 10000; // 自动加载值设置,累计 10000 个时钟周期(1s)后溢出
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 根据 TIM_TimeBaseStructure 中指定的参数初始化 TIM2 里的相关寄存器
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除溢出中断标志
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能 TIM2 中断,允许更新中断
TIM_Cmd(TIM2, ENABLE); // 使能 TIM2 外设
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); // 选择中断优先级分组 0
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 选择配置 TIM2 全局中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级为 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级为 1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能 TIM2 中断通道
NVIC_Init(&NVIC_InitStructure); // 根据以上配置初始化 NVIC
}
void LED_POWER1(int k)
{
switch (k)
{
case 0:
GPIO_ResetBits(GPIOB, GPIO_Pin_4);
GPIO_SetBits(GPIOB, GPIO_Pin_7);
break;
case 1:
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOB, GPIO_Pin_4);
break;
case 2:
GPIO_ResetBits(GPIOB, GPIO_Pin_6);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
break;
case 3:
GPIO_ResetBits(GPIOB, GPIO_Pin_7);
GPIO_SetBits(GPIOB, GPIO_Pin_6);
break;
default:
break;
}
}
void LED_POWER2(int k)
{
switch (k)
{
case 0:
GPIO_ResetBits(GPIOB, GPIO_Pin_7);
GPIO_SetBits(GPIOB, GPIO_Pin_4);
break;
case 1:
GPIO_ResetBits(GPIOB, GPIO_Pin_6);
GPIO_SetBits(GPIOB, GPIO_Pin_7);
break;
case 2:
GPIO_ResetBits(GPIOB, GPIO_Pin_5);
GPIO_SetBits(GPIOB, GPIO_Pin_6);
break;
case 3:
GPIO_ResetBits(GPIOB, GPIO_Pin_4);
GPIO_SetBits(GPIOB, GPIO_Pin_5);
break;
default:
break;
}
}
void LED_POWER3(int k)
{
k %= 2;
switch (k)
{
case 0:
GPIO_ResetBits(GPIOB, GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4);
break;
case 1:
GPIO_SetBits(GPIOB, GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4);
break;
default:
break;
}
}
void INFO(void)
{
printf("creart by cyx 225211 & zxh 225350\r\n");
}
void LED_POWER(int k)
{
switch (dat)
{
case '0':
LED_POWER1(k);
break;
case '1':
LED_POWER2(k);
break;
case '2':
LED_POWER3(k);
break;
case '3':
GPIO_ResetBits(GPIOB, GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4);
break;
case '4':
GPIO_SetBits(GPIOB, GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4);
break;
case '5':
printf("info\r\n");
INFO();
dat = '4';
break;
case 'h':
printf("0 Forward flashing\r\n1 reverse flashing\r\n2 flashing\r\n3 constant lighting\r\n4 off\r\n5 info\r\n");
dat = '4';
break;
default:
printf("default model !\r\nprass 'h' for help......\r\n");
dat = '4';
break;
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) // 1S 时间到,检测到 TIM2 中断
{
led_state++; // 状态加 1
led_state = led_state % 4; // 取模,状态循环从 0 到 4
LED_POWER(led_state); // 控制 LED 状态
TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update); // 清除中断标志位
}
}
int fputc(int ch, FILE *f) // 重定向 printf()函数
{
USART_SendData(USART1, (unsigned char)ch);
// 将 printf()中的内容发往串口
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
// 发送数据寄存器非空时,循环等待
{
}
return (int)ch; // 返回值
}
void USART1_Config(void)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 开启 USART1 和 USART1 中断时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE);
// 配置 USART1 参数
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 初始化 USART1
USART_Init(USART1, &USART_InitStructure);
// 使能 USART1
USART_Cmd(USART1, ENABLE);
// 配置 USART1 中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 设置NVIC优先级分组
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // USART1 中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC
// 使能 USART1 中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 使能 USART1 接收中断
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) // 检查接收中断标志
{
dat = USART_ReceiveData(USART1); // 读取接收到的数据
// 清除接收中断标志
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
说在最后,其实代码由可以改进的空间(其实就是懒得改,一开始写的时候有点赶,就没有想那么多)
可以把led控制函数在主函数调用,在定时器中断里调用时,如果串口传来控制信息,最坏情况需要1s才能实现模式改变(定时器设的1s),放到主函数调用就可以及时响应模式改变了。