一、赛题分析
“双路输出控制器”具有信号输出时间设定、输出信号占空比调整、当前输出通道及时 间显示、系统工作参数存储、串口通讯及 LED 指示等功能。
按照题目要求,PA1、PA2输出相同频率不同占空比PWM波,并且加入按键控制占空比和LED表示相应PWM输出引脚。USART可以定时控制引脚输出PWM波,和输出时间。再此加上EEPROM存储占空比数据。
二、程序设计
程序中的LED、KEY、IIC、TIM4中断初始化都可以在上一篇文章中找到:
蓝桥杯嵌入式——第三届省赛“里程仪”旧板标准库_对愁眠后霜满天的博客-CSDN博客
或者在文章最后直接下载工程,这里不再详细赘述。
1、USART2初始化
在接收中断处,把S定为接收完毕标志位。
#ifndef _USART_H_
#define _USART_H_
#include "stm32f10x.h"
#include "stdio.h"
extern uint8_t RXOVER;
extern uint8_t RXCUNT;
extern u8 USART_RXBUF[20];
void USART2_Init(unsigned long ulBaud);
unsigned char USART_SendChar(USART_TypeDef* USARTx, unsigned char ucChar);
void USART_SendString(USART_TypeDef* USARTx, unsigned char* pucStr);
#endif
#include "stm32f10x_usart.h"
#include "usart.h"
#include "string.h"
uint8_t RXOVER = 0;
uint8_t RXCUNT = 0;
u8 USART_RXBUF[20];
void USART2_Init(unsigned long ulBaud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
// //PA2用作PWM输出,所以把这段注释掉
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
// GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = ulBaud;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx/* | USART_Mode_Tx*/;//这里也是
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2,&USART_InitStructure);
USART_Cmd(USART2,ENABLE);
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
unsigned char USART_SendChar(USART_TypeDef* USARTx, unsigned char ucChar)
{
while(!USART_GetFlagStatus(USARTx,USART_FLAG_TXE));
USART_SendData(USARTx,ucChar);
return ucChar;
}
void USART_SendString(USART_TypeDef* USARTx, unsigned char* pucStr)
{
while(*pucStr != '\0')
USART_SendChar(USARTx,*pucStr++);
}
void USART2_IRQHandler(void)//此处为串口接收中断
{
if(USART_GetITStatus(USART2,USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
USART_RXBUF[RXCUNT++] = USART_ReceiveData(USART2);
if(USART_RXBUF[14] == 'S' || USART_RXBUF[15] == 'S')
{
USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);
RXOVER = 1;
}
}
}
2、PA1、PA2输出相同频率不同占空比PWM波
根据题目要求,PA1、PA2不会同时输出PWM波。一个输出PWM波,另一个输出低电平。
#ifndef _PWM_H
#define _PWM_H
#include "stm32f10x.h"
void TIM2_Configuration(u8 PA1,FunctionalState PA1State, u8 PA2,FunctionalState PA2State);
#endif
/*********************************************
PA1--TIM2--CH2 PA2--TIM2--CH3 PA3--TIM2--CH4
PA6--TIM3--CH1 PA7--TIM3--CH2
*********************************************/
#include "pwm.h"
#include "stm32f10x_tim.h"
void TIM2_Configuration(u8 PA1,FunctionalState PA1State, u8 PA2,FunctionalState PA2State)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
if(PA1 == 0 || PA1State == DISABLE)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIOA->ODR &= ~GPIO_Pin_1;
}else if(PA1 == 10)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIOA->ODR |= GPIO_Pin_1;
}else
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
if(PA2 == 0 || PA2State == DISABLE)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIOA->ODR &= ~GPIO_Pin_2;
}else if(PA2 == 10)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIOA->ODR |= GPIO_Pin_2;
}else
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
TIM_DeInit(TIM2);
TIM_TimeBaseStructure.TIM_Period = 999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel2 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = PA1*100;//占空比设置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
/* PWM1 Mode configuration: Channel3 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = PA2*100;//占空比设置
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
3、RTC时钟
在主函数中初始化后直接调用update_time();显示time_str即可在相应位置显示时钟。
#ifndef __RTC_H
#define __RTC_H
#include "stm32f10x.h"
extern u8 time_str[20];
extern u8 _h,_m,_s;
void RTC_Init(u8 hh,u8 mm,u8 ss);
void RTC_Time_Get(void);
void update_time(void);
void RTC_IRQHandler(void);
#endif
#include "rtc.h"
#include "stdio.h"
#include "stm32f10x_rtc.h"
#include "misc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_pwr.h"
u32 rtctime_value = 0;
void RTC_Init(u8 hh,u8 mm,u8 ss)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
PWR_BackupAccessCmd(ENABLE);
BKP_DeInit();
RCC_LSICmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == 0);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
RCC_RTCCLKCmd(ENABLE);
RTC_ITConfig(RTC_IT_SEC,ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
RTC_WaitForLastTask();
RTC_SetPrescaler(40000 - 1);
RTC_WaitForLastTask();
RTC_SetCounter(hh * 3600 + mm * 60 + ss);
rtctime_value = hh * 3600 + mm * 60 + ss;
RTC_WaitForLastTask();
}
u8 RTC_Flag = 0;
void RTC_Time_Get(void)
{
if(RTC_Flag)
{
RTC_Flag = 0;
rtctime_value = RTC_GetCounter();
}
}
u8 _h = 0,_m = 0,_s = 0;
u8 time_str[20] = {0};
void update_time(void)
{
RTC_Time_Get();
_h = rtctime_value / 3600;
_m = (rtctime_value % 3600) / 60;
_s = rtctime_value % 3600 % 60;
sprintf((char*)time_str, " Timer: %02d:%02d:%02d ", _h, _m, _s);
}
void RTC_IRQHandler(void)
{
u32 times;
if(RTC_GetITStatus(RTC_IT_SEC == 1))
{
times = RTC_GetCounter();
RTC_Flag = 1;
if(times == (24 * 3600 - 1))
{
RTC_SetCounter(0);
RTC_WaitForLastTask();
}
RTC_ClearITPendingBit(RTC_IT_SEC);
}
}
4、main.c
#include "stm32f10x.h"
#include "lcd.h"
#include "led.h"
#include "key.h"
#include "rtc.h"
#include "delay.h"
#include "timer.h"
#include "pwm.h"
#include "i2c.h"
#include "usart.h"
#include "stdio.h"
#include "string.h"
#define PWM_ENABLE 0
#define UART_ENABLE 1
u8 PA1_duty = 8,PA2_duty = 1,channel_flag = 0,flag = 0;
FunctionalState PA1_stat = DISABLE;
FunctionalState PA2_stat = DISABLE;
u8 led_flag = 0,key_flag = 0,usart_flag = 0,arrive_flag = 0;
u8 key1_flag = 0,key3_flag = 0;
u8 string[20],us[20];
u16 time_cnt = 0;
float adc_temp;
void show_main(void);
void usart_proc(void);
void key_in(void);
void PWM_UART_Enable(u8 flag);
//Main Body
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
STM3210B_LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
SysTick_Config(SystemCoreClock/1000);
TIM4_Init();
USART2_Init(9600);
PWM_UART_Enable(1);//处DISABLE PWM,先选择USART ENABLE
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
LED_Init();
KEY_Init();
i2c_init();
RTC_Init(23,59,50);
//存储功能
if(i2c_read(0x11)<=10)
PA1_duty = i2c_read(0x11);
else PA1_duty = 8;
if(i2c_read(0x12)<=10)
PA2_duty = i2c_read(0x12);
else PA2_duty = 1;
while(1)
{
key_in();
show_main();
usart_proc();
if(arrive_flag == 1)//完毕后,PWM全部DISABLE,USART打开,LED全部熄灭,us清0.
{
arrive_flag = 0;
PA1_stat = DISABLE;PA2_stat = DISABLE;
PWM_UART_Enable(1);
channel_flag = 0;
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
LED_Control(LED1|LED2,0);
memset(us,0,sizeof(us));
}
}
}
/*
在key_in中,每按下按键一次,程序只执行一次。在这里选择输出PWM波引脚
因为USART于PWM冲突,在串口打开时,PA2无PWM输出
所以要输出PWM波时要DISABLE USART,同理等待接收数据时要DIASBLE PWM
*/
void key_in(void)
{
key_flag = key_scan();
//KEY1
if(key_flag == 1&&key3_flag == 0)
{
key1_flag =! key1_flag;
if(key1_flag == 0)
{
channel_flag = 0;
LED_Control(LED1,0);
PA1_stat = DISABLE;PA2_stat = DISABLE;
PWM_UART_Enable(1);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
if(key1_flag == 1)
{
channel_flag = 1;
LED_Control(LED1,1);
PA1_stat = ENABLE;PA2_stat = DISABLE;
PWM_UART_Enable(0);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
}
//KEY2
if(key_flag == 2&&key1_flag == 1)
{
PA1_duty = (PA1_duty + 1)%11;
i2c_write(0x11,PA1_duty);Delay_Ms(10);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
//KEY3
if(key_flag == 3&&key1_flag == 0)
{
key3_flag =! key3_flag;
if(key3_flag == 0)
{
channel_flag = 0;
LED_Control(LED2,0);
PA1_stat = DISABLE;PA2_stat = DISABLE;
PWM_UART_Enable(1);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
if(key3_flag == 1)
{
channel_flag = 2;
LED_Control(LED2,1);
PA1_stat = DISABLE;PA2_stat = ENABLE;
PWM_UART_Enable(0);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
}
//KEY4
if(key_flag == 4&&key3_flag == 1)
{
PA2_duty = (PA2_duty + 1)%11;
i2c_write(0x12,PA2_duty);Delay_Ms(10);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
}
void show_main(void)
{
memset(string,0,sizeof(string));
sprintf((char*)string," PWM-PA1: %d %% ",PA1_duty*10);
LCD_DisplayStringLine(Line1,string);
memset(string,0,sizeof(string));
sprintf((char*)string," PWM-PA2: %d %% ",PA2_duty*10);
LCD_DisplayStringLine(Line3,string);
update_time();
LCD_DisplayStringLine(Line5,(u8*)time_str);
switch(channel_flag)
{
case 0: LCD_DisplayStringLine(Line6,(u8*)" Channel: ");break;
case 1: LCD_DisplayStringLine(Line6,(u8*)" Channel: PA1 ");break;
case 2: LCD_DisplayStringLine(Line6,(u8*)" Channel: PA2 ");break;
default: break;
}
LCD_DisplayStringLine(Line8,(u8*)" Command: ");
memset(string,0,sizeof(string));
sprintf((char*)string," %s ",us);
LCD_DisplayStringLine(Line9,string);
if(*us == 0)
LCD_DisplayStringLine(Line9,(u8*)" None ");
}
/*
串口接收函数,把接收到的数据幅值给 us,然后清零USART_RXBUF
只有us响应后us清0后再次打开串口接收
*/
void usart_proc(void)
{
u8 i;
if(RXOVER == 1 && *us == 0)
{
RXOVER = 0;
RXCUNT = 0;
for(i = 0;i<20;i++)
{
us[i]=USART_RXBUF[i];
if(us[i] == 'S')break;
}
memset(USART_RXBUF,0,sizeof(USART_RXBUF));
}
if((us[0]-48)*10+us[1]-48==_h&&(us[3]-48)*10+us[4]-48==_m&&(us[6]-48)*10+us[7]-48==_s&&us[14] == 'S')
{
if(us[11]-48 == 1)//满足条件后,关闭USART,选择PWM输出
{
channel_flag = 1;
PA1_stat = ENABLE;PA2_stat = DISABLE;
PWM_UART_Enable(0);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}else if(us[11]-48 == 2)
{
channel_flag = 2;
PA1_stat = DISABLE;PA2_stat = ENABLE;
PWM_UART_Enable(0);
TIM2_Configuration(PA1_duty,PA1_stat,PA2_duty,PA2_stat);
}
time_cnt = (us[13]-48)*1000;//倒计时时间
usart_flag = 1;//中断开始标志位
}
if(usart_flag == 1)
{
if(channel_flag == 1)LED_Control(LED1,1);
if(channel_flag == 2)LED_Control(LED2,1);
}
}
//USART PWM 选择函数,如果两者冲突则PA2无PWM输出
void PWM_UART_Enable(u8 flag)
{
if(flag==PWM_ENABLE)
{
USART_Cmd(USART2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB2Periph_AFIO, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB2Periph_AFIO, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
if(flag==UART_ENABLE)
{
TIM_Cmd(TIM2, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB2Periph_AFIO, DISABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB2Periph_AFIO, ENABLE);
USART_Cmd(USART2, ENABLE);
}
}
void TIM4_IRQHandler(void)
{
static u16 T_cnt = 0;
if(TIM_GetITStatus(TIM4,TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
if(usart_flag == 1)
{
if(++T_cnt>=time_cnt)
{
usart_flag = 0;
T_cnt = 0;
arrive_flag = 1;//倒计时完毕标志位
}
}
}
}
三、附上工程
链接:https://pan.baidu.com/s/1Fg3Uevz-e38MwdstCw-GuA
提取码:fuie
有不对的地方,请多指教...