蓝桥杯嵌入式学习总结(旧版)

2022/5/25:

还愿省一

重要引脚

GPIO功能
PA1TIM2_CH2 / SER(74LS595串行数据输入引脚) / 频率可调脉冲
PA2TIM2_CH3 / USART2_TX / RCK(74LS595串行存储时钟输入引脚) / 频率可调脉冲
PA3USART2_RX / SCK(74LS595串行移位时钟输入引脚) / 光敏电阻读入引脚
PA4ADC1_IN4 / SCL(三轴传感器时钟引脚)
PA5ADC1_IN5 / SDA(三轴传感器数据引脚) / 4*2按键矩阵
PA6TIM3_CH1 / 占空比可调脉冲 / 温度传感器
PA7TIM3_CH2 / 占空比可调脉冲 / 温湿度传感器

    

(旧版)初始化配置
1.KEY,LED与TIM4必考
KEY         浮空输入(RCC/GPIO)
LED         推挽输出(RCC/GPIO)
TIM4        配置(NVIC/TimeBase/IT)
led,tim,key配置控制在20分钟内

PWM输出     配置(RCC/NVIC/GPIO/TimeBase/TimOC/IT),中断配置duty,flag,capture
PWM输出配置 控制在8分钟以内  TIM_GetCapture()  与 TIM_SetCompare()
PWM捕获     中断函数中      TIM_SetCounter()  与 TIM_GetCounter()
USART配置   (2个RCC初始化/GPIO/USART/NVIC)
ADC配置     注意将ENABLE改成DISABLE PB0 通道0 注意get_adc()配置
RTC配置     RTC,NVIC初始化  Time_Adjust Time_Display RTC_IRQHandler()
ADC,RTC控制在4分钟

     

文件配置

     例程文件配置过程中要在Library中加入libraries文件

1.LED

void led_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    /* GPIOD Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);
  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
	
  /* Configure PD0 and PD2 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9| GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15  ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

}

void led_control(u16 led,u8 state)
{  
	if(state==1)
	{
   GPIO_ResetBits(GPIOC,led<<8);	
   GPIO_SetBits(GPIOD,GPIO_Pin_2);
	 GPIO_ResetBits(GPIOD,GPIO_Pin_2);
	}		
  else
	{
   GPIO_SetBits(GPIOC,led<<8);	
   GPIO_SetBits(GPIOD,GPIO_Pin_2);
	 GPIO_ResetBits(GPIOD,GPIO_Pin_2);

  }
}

令pd2置高(1)后,输入是什么,输出就是什么,把pd2再置低(0),输入是什么,输出保持不变
起到锁存器的功能,但一般程序里只要是pd2置高就可以了,不需要再置低,即不需要锁存功能
 

2.Buzzer

当我们stm32复位后,PB4引脚默认是作为JTAG接口的RST引脚,因此我们再使用蜂鸣器之前要先把PB4复用回我们的普通IO口。

因此就要加上这条语句

GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST,ENABLE);
同时我们还要使能复用IO的时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOB,ENABLE);
接下来蜂鸣器的用法就跟我们的普通LED是一样的了。

void buzzer_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    /* GPIOD Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 ;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);
	
}


3.AT24c02

I2C代码需要在参考例程里面复制粘贴,需要自己编写的是AT24C02代码

在这里插入图片描述

void Write_AT24c02(unsigned char add,unsigned char data1)
{
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(add);
	I2CWaitAck();
	I2CSendByte(data1);
	I2CWaitAck();
	I2CStop();
}

 在这里插入图片描述

unsigned char Read_AT24c02(unsigned char add)
{
  unsigned char temp;
	I2CStart();
	I2CSendByte(0xa0);
	I2CWaitAck();
	I2CSendByte(add);
	I2CWaitAck();
	I2CStart();
	I2CSendByte(0xa1);
	I2CWaitAck();
	temp=I2CReceiveByte();
	I2CWaitAck();
	I2CStop();
	return temp;

}

使用AT24c02写代码时候要延迟5ms

E2PROM一共有256个字节

如果要存储u16位数据,要用2个地址来分来存储。/256储存高八位,%256存储低八位

利用共同体储存不同数据类型

// 声明共用体eeprom_float
union eeprom_float
{
   float a;  
   u8 b[4];
}float_write,float_read;

// 将共用体中的数据以字节的方式存放到eeprom中
for(i=0;i<sizeof(float);i++)
{
	Write_AT24c02(0x10+i,float_write.b[i]);
	Delay_Ms(5);
}
for(i=0;i<sizeof(float);i++)
{
	float_read.b[i]=Read_AT24c02(0x10+i);
}

// 此时共用体float_read.a就等于float_write.a,而不需要在存放和读取的时候进行数据类型的转换
sprintf(str,"float_read=%f",float_read.a);
LCD_DisplayStringLine(Line0,str);

/*  
不同类型数据 需要根据字节大小 改变数组长度
u8 u16 u32 s16 float double 
1  2   4   2   4     8
字节大小
*/

省赛中会遇到赋初值情况,第一次读取是初值,后面掉电读取是修改值

	if(Read_AT24c02(0x06)!=2&&Read_AT24c02(0xaa)!=15)   //地址和数值都是瞎写的
	{
				Write_AT24c02(0x01,30);				Delay_MS(5);
				Write_AT24c02(0x02,50);				Delay_MS(5);
				Write_AT24c02(0x03,70);				Delay_MS(5);	
		
				Write_AT24c02(0x06,2); //加了这个语句之后,就可以让这个判断只执行一次				
				Delay_MS(5);
				Write_AT24c02(0xaa,15); 			
				Delay_MS(5);
	}	

4.USART

stm32f10x_stdperiph_lib\STM32F10x_StdPeriph_Lib_V3.5.0\Utilities\STM32_EVAL\STM3210B_EVAL (328行)

STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART\Printf

DMA_Interrupt

DMA_Interrupt\stm32f10x_it.c(中断函数)

void STM_EVAL_COMInit( USART_InitTypeDef* USART_InitStruct)
{
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Enable GPIO clock */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA| RCC_APB2Periph_AFIO, ENABLE);

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  /* Configure USART Tx as alternate function push-pull */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);


  /* Configure USART Rx as input floating */
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* USART configuration */
  USART_Init(USART2, USART_InitStruct);
    
  /* Enable USART */
  USART_Cmd(USART2, ENABLE);
}
void USART2_Init(void)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  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;

  STM_EVAL_COMInit(&USART_InitStructure);


  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //注意加上最后一句话

}

PS:第一个字符打印不出来

直接修改while的等待条件,while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);把TC改为TXE,因为TXE只能硬件清零,在数据写进DR时就置0,数据移出时置1,可以保证数据不会在DR被覆盖;

int fputc(int ch, FILE *f)
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART */
  USART_SendData(USART2, (uint8_t) ch);

  /* Loop until the end of transmission */
  while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET)
  {}

  return ch;
}

u8 rx_buf[10];
u8 rx_count=0;
void USART2_IRQHandler(void)
{
  if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  {
    /* Read one byte from the receive data register */
    USART_ClearITPendingBit(USART2, USART_IT_RXNE);
	rx_buf[rx_count++]=USART_ReceiveData(USART2);
  }
}

使用printf注意:1.打开 Use MicroLIB2.#include"stdio.h"

IDLE空闲中断接收不定长数据

USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//记得加上

void USART2_IRQHandler(void)
{
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
	{
		rx_buf[rx_count++]=USART_ReceiveData(USART2);
	}
	else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
	{
		USART_ReceiveData(USART2);
		printf("The length:%d\r\n",rx_count);
		printf("The data:%s\r\n",rx_buf);
		memset(rx_buf,0,sizeof(rx_buf));
		rx_count=0;
		USART_ClearITPendingBit(USART2,USART_IT_IDLE);
  }
}

5.LCD

LCD操作常用函数

  STM3210B_LCD_Init(); //初始化LCD相关IO,并对液晶进行初始化

  LCD_Clear(Blue);//清屏操作

  LCD_SetBackColor(Blue);//设置显示文字的背景色

  LCD_SetTextColor(White);//设置字体颜色

  u8 str[20];
  sprintf(str,"helloworld");
  LCD_DisplayStringLine(Line2,str);

  //在第二行输出helloworld
  

  void Display_val(u16 val)
{
    u8 temp[20];
	sprintf(temp,"val:%5d",val);
	LCD_DisplayStringLine(Line1,temp);
}

  Display_val(77);//在第一行输出77

使用sprintf 注意添加#include "stdio.h"

关于LCD与LED冲突问题

方法一:

void LED_control_ALL(u16 flagstatus)
{
	  GPIOC->ODR=flagstatus<<8;
	  GPIO_SetBits(GPIOD,GPIO_Pin_2);
      GPIO_ResetBits(GPIOD,GPIO_Pin_2);

}
//使用这个函数点灯 可以避免LED冲突
//输入LED_control_ALL(0xfe) 点亮第一个灯

方法二:

void LCD_WriteRAM_Prepare(void)
void LCD_WriteRAM(u16 RGB_Code)
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue)
开头加上 u16 pcout =GPIOC->ODR  
结尾加上 GPIOC->ODR=pcout   
即可

关于LCD长数据对短数据覆盖问题

方法一:在前面空格

方法二:格式化输出,针对数据

	sprintf((char *)str,"%5dHz",5000);
	LCD_DisplayStringLine(Line1,str);
	sprintf((char *)str,"%5dHz",10);
	LCD_DisplayStringLine(Line1,str);
    //如果是%d则会是10HzHz

	sprintf((char *)str,"%-5dHz",10);
	LCD_DisplayStringLine(Line2,str);
    //数据左对齐

    sprintf((char *)str,"%05dHz",10);
	LCD_DisplayStringLine(Line3,str);
    //数据前面补0
    
    //%x输出十六进制  %o输出八进制 %%输出百分号 %s输出字符串 %c输出字符  %.3f输出小数点后三位

关于LCD字符闪烁与高亮,LCD背景高亮

LCD高亮会用到LCD_Displaychar函数 板子上LCD一行有320个参数,显示一个字符需要16个

_Bool lcd_flag=0;

  if(lcd_flag==1)
	{
        memset(lcd_buf,0,sizeof(lcd_buf));
        sprintf((char *)lcd_buf,"      %0.2d:%0.2d:%0.2d",Report_HH, Report_MM, Report_SS);
	    LCD_DisplayStringLine(Line5,lcd_buf);
     }
else		
	{
         memset(lcd_buf,0,sizeof(lcd_buf));
         sprintf((char *)lcd_buf,"        :%0.2d:%0.2d",Report_MM, Report_SS);
	     LCD_DisplayStringLine(Line5,lcd_buf);
     }

//实现LCD字符闪烁 lcd_flag是定时器4设置flag 每0.3s翻转一次



        LCD_SetTextColor(Red);//设置字符显示颜色
        LCD_DisplayChar(Line4,200,Report_HH/10+'0');                            
	    LCD_DisplayChar(Line4,200-16,Report_HH%10+'0');
		LCD_SetTextColor(White);//显示单个字符的显示函数

//实现字符变红


       	LCD_SetBackColor(Green);
        LCD_DisplayStringLine(Line2,lcd_buf);
        LCD_SetBackColor(Blue);

//实现背景高亮

6.ADC

 

    PB0对应着ADC的Channel_8

    STM32F10x_StdPeriph_Examples\ADC\ADC1_DMA 

    

void adc_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; 
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfChannel = 1;
	ADC_Init(ADC1, &ADC_InitStructure);
    //初始化的时候注意要把ENABLE改为DISABLE

	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);    
    //要注意是ADC1和第八通道

	ADC_Cmd(ADC1, ENABLE);   
	ADC_ResetCalibration(ADC1);

	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);

	while(ADC_GetCalibrationStatus(ADC1));
}
u16 get_adc()
{
	u16 value;
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(!ADC_GetFlagStatus(ADC1,  ADC_FLAG_EOC));
	value = ADC_GetConversionValue(ADC1);
	
	return value;
}
	while(1)
	{
	   adc_value=get_adc();    
       sprintf(str,"adc: %d",(adc_value/(4096/4))); //分档位 0 1 2 3
       LCD_DisplayStringLine(Line2,str);
	   Delay_Ms(100);
	}
//float(adc_value)/4096*3.3 0~3.3V

ADC模块读到的数据是12位的数据,STM32读到的ADC值,是从0到4095。当把ADC引脚接了GND,读到的是0;当把ADC引脚接了VDD,读到的是4095。

tmp=(float)adc_value/4096*3.3

注意事项

关于get_adc()函数,当使用ADC_SoftwareStartConvCmd()使能软件转换之后,需要使用ADC_GetFlagStatus()函数等待转换结束后,再进行ADC_GetConversionValue()输出

关于adc_init()函数,初始化的时候注意要把ENABLE改为DISABLE,ADC_RegularChannelConfig() 改成第八通道

7.RTC时钟

注意事项

1.RTC配置的时候结合要选择:LSI内部晶振

2.调用配置NVIC函数

3.中断处理函数

需要用到的文件

RTC/LSI_Calib/main.c (178开始

void RTC_Configuration(void)
{
  /* Enable PWR and BKP clocks */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

  /* Allow access to BKP Domain */
  PWR_BackupAccessCmd(ENABLE);

  /* Reset Backup Domain */
  BKP_DeInit();

  /* Enable the LSI OSC */
  RCC_LSICmd(ENABLE);
  /* Wait till LSI is ready */
  while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
  {}
  /* Select the RTC Clock Source */
  RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);

  /* Enable RTC Clock */
  RCC_RTCCLKCmd(ENABLE);

  /* Wait for RTC registers synchronization */
  RTC_WaitForSynchro();

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Enable the RTC Second */
  RTC_ITConfig(RTC_IT_SEC, ENABLE);

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* Set RTC prescaler: set RTC period to 1sec */
  RTC_SetPrescaler(40000);

  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();

  /* To output second signal on Tamper pin, the tamper functionality
       must be disabled (by default this functionality is disabled) */
  BKP_TamperPinCmd(DISABLE);

  /* Enable the RTC Second Output on Tamper Pin */
  // BKP_RTCOutputConfig(BKP_RTCOutputSource_Second);  
  //屏蔽最后一句话,解决白屏问题
  NVIC_Configuration();
}
//最后需要加上NVIC_Configuration

RTC/Calendar/main.c 

在Time_Display函数中加入TimeVar=0 和 改为0x00015180  会显示出00:00:00

void RTC_IRQHandler(void)
{
  if (RTC_GetITStatus(RTC_IT_SEC) != RESET)
  {
    /* Clear the RTC Second interrupt */
    RTC_ClearITPendingBit(RTC_IT_SEC);


    /* Enable time update */
    TimeDisplay = 1;

    /* Wait until last write operation on RTC registers has finished */
    RTC_WaitForLastTask();
    
  }
}

void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  /* Configure one bit for preemption priority */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  /* Enable the RTC Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void Time_Display(uint32_t TimeVar)
{
	u8 str[20];
  uint32_t THH = 0, TMM = 0, TSS = 0;
  
  /* Reset RTC Counter when Time is 23:59:59 */
  if (RTC_GetCounter() == 0x0001517F) 
  {
     RTC_SetCounter(0x0);
     /* Wait until last write operation on RTC registers has finished */
     RTC_WaitForLastTask();
  }
  
  /* Compute  hours */
  THH = TimeVar / 3600;
  /* Compute minutes */
  TMM = (TimeVar % 3600) / 60;
  /* Compute seconds */
  TSS = (TimeVar % 3600) % 60;

    sprintf(str,"Time: %0.2d:%0.2d:%0.2d", THH, TMM, TSS);
	LCD_DisplayStringLine(Line1,str);
}
//需要自己修改的只有后俩句

void Time_Adjust(u8 HH,u8 MM,u8 SS)
{
  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
  /* Change the current time */
  RTC_SetCounter(HH*3600+MM*60+SS);
  /* Wait until last write operation on RTC registers has finished */
  RTC_WaitForLastTask();
}

/*
.......
  while (1)
  {
    /* If 1s has been elapsed */
    if (TimeDisplay == 1)
    {
      /* Display current time */
      Time_Display(RTC_GetCounter());
      TimeDisplay = 0;
    }
  }
.......
*/

8.PWM

ARR的周期值对应   TIM_TimeBaseStructure.TIM_Period  的设置值

PSC的分频值对应   TIM_TimeBaseStructure.TIM_Prescaler 的设置值

注:ARP及PSC的设置值都是减1的,所以这里要再加上1,才能还原单片机内部的处理值。   

PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) -1 理解

    现在我们知道定时器时钟的频率是72MHz,SystemCoreClock是72MHz,则PrescalerValue计算出来是71,我们将预分频器设置为71,查看数据手册,如下图,可以知道会对时钟的频率进行72分频,得到的频率是1MHz。然后把TIM_Period 设置为1000-1,即设置ARR(自动重装载寄存器)为999,即定时器计数器从0开始,计数到999,则会自动重新装载,前面我们得到了频率为1MHz,那么定时器每进行一次重转载的周期是1MHz/1000=1kHz,也就是1ms。

不同频率,不同占空比

STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\OCToggle

__IO uint16_t TIM2_CCR2_Val = 1000;
__IO uint16_t TIM2_CCR3_Val = 2000;
void TIM2_PWM_OCToggle(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  uint16_t PrescalerValue = 0;

  /* System Clocks Configuration */
  /* TIM2 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  /* GPIOA clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|
                         RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);

  /* NVIC Configuration */
  /* Enable the TIM2 global Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  /* GPIO Configuration */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
  /* ---------------------------------------------------------------------------
    TIM2 Configuration: Output Compare Toggle Mode:
    TIM2CLK = SystemCoreClock / 2,
    The objective is to get TIM2 counter clock at 12 MHz:
     - Prescaler = (TIM2CLK / TIM2 counter clock) - 1
    CC1 update rate = TIM2 counter clock / CCR1_Val = 366.2 Hz
    CC2 update rate = TIM2 counter clock / CCR2_Val = 732.4 Hz
    CC3 update rate = TIM2 counter clock / CCR3_Val = 1464.8 Hz
    CC4 update rate = TIM2 counter clock / CCR4_Val = 2929.6 Hz
  ----------------------------------------------------------------------------*/
  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;

  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 65535;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  /* Output Compare Toggle Mode configuration: Channel2 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = TIM2_CCR2_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC2Init(TIM2, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* Output Compare Toggle Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = TIM2_CCR3_Val;

  TIM_OC3Init(TIM2, &TIM_OCInitStructure);

  TIM_OC3PreloadConfig(TIM2, TIM_OCPreload_Disable);

  /* TIM enable counter */
  TIM_Cmd(TIM2, ENABLE);
  
  /* TIM IT enable */
  TIM_ITConfig(TIM2, TIM_IT_CC2 | TIM_IT_CC3 , ENABLE);
}

uint16_t TIM2_capture = 0;
_Bool TIM2_CH2_flag=0, TIM2_CH3_flag=0;
float TIM2_CH2_duty=0.3, TIM2_CH3_duty=0.7;
void TIM2_IRQHandler(void)
{
	/* TIM2_CH2 toggling with frequency = 732.4 Hz */
	if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_CC2 );
		TIM2_capture = TIM_GetCapture2(TIM2);
		if(TIM2_CH2_flag==1)
		{
			TIM_SetCompare2(TIM2, TIM2_capture + (u16)(TIM2_CCR2_Val*TIM2_CH2_duty));
			TIM2_CH2_flag=0;
		}
		else
		{
			TIM_SetCompare2(TIM2, TIM2_capture + (u16)(TIM2_CCR2_Val*(1-TIM2_CH2_duty)));
			TIM2_CH2_flag=1;
		}
	}

  /* TIM2_CH3 toggling with frequency = 1464.8 Hz */
  if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    TIM2_capture = TIM_GetCapture3(TIM2);
	if(TIM2_CH3_flag==1)
	{
		TIM_SetCompare3(TIM2, TIM2_capture + (u16)(TIM2_CCR3_Val*TIM2_CH3_duty));
		TIM2_CH3_flag=0;
	}
	else
	{
		TIM_SetCompare3(TIM2, TIM2_capture + (u16)(TIM2_CCR3_Val*(1-TIM2_CH3_duty)));
		TIM2_CH3_flag=1;
	}
  }
}
   while(1)
 {
    TIM2_CH2_duty=0.3;
	TIM2_CH3_duty=0.7;		
 }

频率和占空比的计算:由于我设定为TIM2_CCR3_Val=2000,主频为1Mhz,输出PWM赫兹为500,占空比为0.7,通道二输出PWM赫兹为1000,占空比为0.3

PWM捕获占空比和频率

Project\STM32F10x_StdPeriph_Examples\TIM\InputCapture  初始化链接

原理:

首先把捕获的模式设置为上升沿捕获,发生第一次捕获到上升沿的中断,以此中断时刻作为一个起点,得到的计数值清0,此时将捕获模式设置为下降沿捕获,在发生第二次中断的时候,捕获到了下降沿,得到的计数值为TIM3_CH2_ReadValue1就是一个周期中高电平的时间,然后我们在中断中又将捕获的方式设置为上升沿捕获,那么在第三次产生中断的时候,得到的计数值为TIM3_CH2_ReadValue2。所以到此为止,我们知道了一个周期中高电平和整个周期的时间,那么这个PWM方波的频率和占空比就得到了

PWM方波的频率应该为72MHz/Ch2_ReadValue2

PWM方波的占空比应该为Ch2_ReadValue1*100/Ch2_ReadValue2

//乘一百是为了让占空比显示0-100 而不是小数

数据溢出的问题
首先我要进行捕获的方波是1000Hz,但是捕获得到的数据始终是不对的,那么我们来算一算对于1000Hz的方波,一个周期就是1ms,那么对于72MHz,1ms的时间计数会是多少呢,就应该是72000,而计数器CNT是16位的,最大为65535,那肯定会发生溢出了啦,那么我们就找到了问题的根源。所以我的解决方法是,将定时器的更新中断打开,设置一个count,也就是每次数据溢出会触发更新中断,让count++,然后在将捕获得到的Value加上65536*count

Ch2_Readvalue1 =  65536 * Ch2_Update_Cnt + TIM_GetCounter(TIM3)

Ch2_Readvalue2 =  65536 * Ch2_Update_Cnt + TIM_GetCounter(TIM3)

void TIM3_Input_Mode_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_ICInitTypeDef  TIM_ICInitStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6|GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;                                                                        
  TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
  TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
  TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
  TIM_ICInitStructure.TIM_ICFilter = 0x0;

  TIM_ICInit(TIM3, &TIM_ICInitStructure);
  
  TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //容易忘记通道一
  TIM_ICInit(TIM3, &TIM_ICInitStructure);
	
  /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);

  /* Enable the CC2 Interrupt Request */
  TIM_ITConfig(TIM3, TIM_IT_CC1|TIM_IT_CC2|TIM_IT_Update, ENABLE);
}
uint32_t CH2_ReadValue1 = 0, CH2_ReadValue2 = 0;
uint32_t ch2_CaptureNumber = 0;
uint32_t Capture = 0;
uint32_t TIM3_CH2_Freq = 0;
uint32_t TIM3_CH2_Duty = 0;
uint32_t Ch2_Update_Cnt = 0;

uint32_t CH1_ReadValue1 = 0, CH1_ReadValue2 = 0;
uint32_t ch1_CaptureNumber = 0;
uint32_t TIM3_CH1_Freq = 0;
uint32_t TIM3_CH1_Duty = 0;
uint32_t Ch1_Update_Cnt = 0;

u8 capture_flag=1;


void TIM3_IRQHandler(void)
{ 
	if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) 
  {
		/* Clear TIM3 Update compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
		Ch2_Update_Cnt++;
		Ch1_Update_Cnt++;
	}
	
  if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET) 
  {
    /* Clear TIM3 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
		if(capture_flag==1)
		{
			if(ch2_CaptureNumber == 0)
			{
				/* Get the Input Capture value */
				TIM_SetCounter(TIM3, 0);
				Ch2_Update_Cnt = 0;
				ch2_CaptureNumber = 1;
				TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Falling);	// ÅäÖÃΪϽµÑØ
			}
			else if(ch2_CaptureNumber == 1)
			{
				/* Get the Input Capture value */
				CH2_ReadValue1 = TIM_GetCounter(TIM3) + 65536*Ch2_Update_Cnt;
				ch2_CaptureNumber = 2;
				TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Rising);	// ÅäÖÃΪÉϽµÑØ
			}
			else if(ch2_CaptureNumber == 2)
			{
				/* Get the Input Capture value */
				CH2_ReadValue2 = TIM_GetCounter(TIM3) + 65536*Ch2_Update_Cnt; 
				
				/* Frequency computation */
		    TIM3_CH2_Freq = (uint32_t) (SystemCoreClock * 1.0 / CH2_ReadValue2 + 0.5);
			TIM3_CH2_Duty = (uint32_t) (CH2_ReadValue1 * 100.0 / CH2_ReadValue2 + 0.5);
				ch2_CaptureNumber = 0;
			}
     }
  }
	
	  if(TIM_GetITStatus(TIM3, TIM_IT_CC1) == SET) 
  {
    /* Clear TIM3 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
		if(capture_flag==2)
		{
			if(ch1_CaptureNumber == 0)
			{
				/* Get the Input Capture value */
				TIM_SetCounter(TIM3, 0);
				Ch1_Update_Cnt = 0;
				ch1_CaptureNumber = 1;
				TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);	// ÅäÖÃΪϽµÑØ
			}
			else if(ch1_CaptureNumber == 1)
			{
				/* Get the Input Capture value */
				CH1_ReadValue1 = TIM_GetCounter(TIM3) + 65536*Ch1_Update_Cnt;
				ch1_CaptureNumber = 2;
				TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Rising);	// ÅäÖÃΪÉϽµÑØ
			}
			else if(ch1_CaptureNumber == 2)
			{
				/* Get the Input Capture value */
				CH1_ReadValue2 = TIM_GetCounter(TIM3) + 65536*Ch1_Update_Cnt; 
				
				/* Frequency computation */
		 TIM3_CH1_Freq = (uint32_t) (SystemCoreClock * 1.0 / CH1_ReadValue2 + 0.5);
		TIM3_CH1_Duty = (uint32_t) (CH1_ReadValue1 * 100.0 / CH1_ReadValue2 + 0.5);
				ch1_CaptureNumber = 0;
			}
	 }
  }
}
void TIM4_IRQHandler(void)
{
	static u16 pwm_capture_count=0;;
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
		pwm_capture_count++;
   if(pwm_capture_count==100)
		{
			pwm_capture_count=0;
			capture_flag = capture_flag % 2 + 1;
			TIM3_CH1_CaptureNumber=0;
			TIM3_CH2_CaptureNumber=0;
		}
  }
}

9.KEY

输出模式是浮空输出

void key_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
    /* GPIOD Periph clock enable */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
  
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8  ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
	
  /* Configure PD0 and PD2 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 ;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING
  GPIO_Init(GPIOB, &GPIO_InitStructure);

}
unsigned char Trg;
unsigned char Cont;
void key_read(void)
{
  unsigned char ReadData = (KEYPORT)^0xff;
  Trg = ReadData & (ReadData ^ Cont);
  Cont = ReadData;
}
/*(1) 没有按键的时候       ReadData = 0    Trg = 0    Cont = 0
  (2) 第一次PB0按下的情况  ReadData = 0x01 Trg = 0x01 Cont = 0x01
  (3) PB0长按             ReadData = 0x01 Trg = 0    Cont = 0x01
  (4) 按键松开的情况       ReadData = 0    Trg = 0    Cont = 0x00
*/

key.h

#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"
void key_init(void);
void key_read(void);

#define KB1 	GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)
#define KB2 	GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_8)
#define KB3 	GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)
#define KB4 	GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_2)

#define KEYPORT KB1 | (KB2 << 1) | (KB3 << 2) | (KB4 << 3) | 0xf0

extern unsigned char Trg;
extern unsigned char Cont;

#endif 

需要自己配置  记住三行按键代码 需要多练习

关于按键长短按判断

int main(void)
{
 ......//初始化
	while(1)
	{
		if(key_flag)
      {
				key_flag=0;//定时器4 10ms判断一次
				key_read();//三行代码	
				if(Cont == 0x01) //按键按下判断
				{
					key_number++;
					if(key_number>=50&&key_false==1) //50 * 10 ms = 500ms
					{
						key_false=0;
						key_number=0;
						lcd_number+=10;
                     }
                }
				if(Cont ==0x00 && Trg ==0x00) //松手监测
				{
					if(key_number>0 && key_number<50 && key_false==1)
					{
						  lcd_number++;
                    }
                      key_number=0;
				      key_false=1;
                }
	 }
	}
}

10.TIM

在比赛提供的V3.5库的"Project->STM32F10x_StdPeriph_Examples->TIM->TimeBase"文件夹

void tim4_init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; 
  NVIC_InitTypeDef NVIC_InitStructure;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
  NVIC_Init(&NVIC_InitStructure);
	
  TIM_TimeBaseStructure.TIM_Period =999 ;       
  TIM_TimeBaseStructure.TIM_Prescaler = 71;     //72 000 000 /72 = 1 000 000 
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

  TIM_ITConfig(TIM4,TIM_IT_Update, ENABLE);

  TIM_Cmd(TIM4, ENABLE);

}

​
void TIM4_IRQHandler(void)
{
	static u16 buzzer_count =0;
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    buzzer_count++;
		if(buzzer_count==500)
		{
			buzzer_count=0;
			buzzer_flag=1; 
    }
  }
}

​
  • 中断设置改为TIM_IT_Update
  • 时钟设置为TIM4    

 使用TIM4原因

TIM2,TIM3做硬件PWM输出和捕获时会用到,TIM1是高级定时器。所以用Tim4

PWM与USART冲突

由于usart接受引脚为PA2,而PWM2的输出引脚也是PA2这就导致了,当我们需要PWM波时需要关闭USART功能,即失能usart的接受中断,开启命令和时钟

#define PWM_ENABLE 	1
#define UART_ENABLE 2
void PWM_UART_Enable(u8 flag)
{
	if(flag==PWM_ENABLE)
	{
		USART_Cmd(USART2, DISABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, DISABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	
		TIM_Cmd(TIM2, ENABLE);
	}
	
	if(flag==UART_ENABLE)
	{
	    TIM_Cmd(TIM2, DISABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, DISABLE);		
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
		USART_Cmd(USART2, ENABLE);
	}
}

省赛代码

gitee 代码:https://gitee.com/yin-shijie111/code.git


第十二届第一场比赛题目分析

这届题目主要难度在于串口部分,其他部分常规没有什么特别难点。我们先看看串口部分题目

/*
主要思路:1.首先对接收到数据进行判断,查看是否符合格式
        2.查找接收数组的id,如果查找到就进行结算,没找到就进入第三步
        3.查找是否有空位,如果有空位赋值,没空位输出Error
下面是代码实现
*/

第八届省赛思路分析

/* 
思路: 第一层开始
         1.如果有设定的层被按下
         2.关门(4s)
         3.往第二层移动(6s)
         4.到达第二层
         5.判断第二层是不是设定层 -> 如果 是则开门4s-> 继续判断有没有存在其他目标
                                                 -> 有 :停留2s. 关门4s,继续移动
                                                 -> 否 :保持开门状态,直到有其他设定平台被按下
         6.到达第三层
         7.判断第三层是不是设定层(与第二层判断方法一致)
         8.到达第四层,第四层则一定是设定层
*/

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值