蓝桥杯嵌入式学习总结

 重要引脚

GPIO 功能
PA1 TIM2_CH2 / SER(74LS595串行数据输入引脚) / 频率可调脉冲
PA2 TIM2_CH3 / USART2_TX / RCK(74LS595串行存储时钟输入引脚) / 频率可调脉冲
PA3 USART2_RX / SCK(74LS595串行移位时钟输入引脚) / 光敏电阻读入引脚
PA4 ADC1_IN4 / SCL(三轴传感器时钟引脚)
PA5 ADC1_IN5 / SDA(三轴传感器数据引脚) / 4*2按键矩阵
PA6 TIM3_CH1 / 占空比可调脉冲 / 温度传感器
PA7 TIM3_CH2 / 占空比可调脉冲 / 温湿度传感器

    

PBO ADC1_Channel8
PA0 key1
PA8 key2
PB1 key3
PB2 key4

     

0.文件配置

     例程文件配置过程中要在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);				DelayMS(5);
				Write_AT24c02(0x02,50);				DelayMS(5);
				Write_AT24c02(0x03,70);				DelayMS(5);	
		
				Write_AT24c02(0x06,2); //加了这个语句之后,就可以让这个判断只执行一次				
				DelayMS(5);
				Write_AT24c02(0xaa,15); 			
				DelayMS(5);
	}	

4.串口通信

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"

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字符闪烁与高亮

_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翻转一次


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

//实现字符变红

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

     需要用到文件:     以上是常用GPIO

     Project\STM32F10x_StdPeriph_Examples\TIM\PWM_Output

    PWM输出相同频率,不同占空比

// 设置输出比较寄存器的值,用于改变PWM的占空比。
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
// 用于设置ARR的值,通过设置ARR的值来改变定时器的频率
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)

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

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

void TIM2_PWM_Init(void)
{
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	uint16_t PrescalerValue = 0;
	uint16_t CCR1_Val = 500;
	

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

	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);
	

	PrescalerValue = (uint16_t) (SystemCoreClock / 1000000) - 1;

	TIM_TimeBaseStructure.TIM_Period = 1000 - 1;
	TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;		
	TIM_OCInitStructure.TIM_Pulse = CCR1_Val;							
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;			
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);
	TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
	

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;					
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;		
	TIM_OCInitStructure.TIM_Pulse = CCR1_Val;							
	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);
}

注: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。

while(1)
{
   TIM_SetAutoreload(TIM2,1000-1); //pwm频率为1khz
   TIM_SetCompare2(TIM2,500);      //PA1(TIM2_CH2)占空比为50%
   TIM_SetCompare3(TIM2,100);      //PA2(TIM2_CH3)占空比为10%
}

不同频率,固定占空比

STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\TIM\OCToggle

__IO uint16_t TIM3_CCR1_Val = 32768;
__IO uint16_t TIM3_CCR2_Val = 16384;
void tim3_pwm_octoggle(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	uint16_t PrescalerValue = 0;
	
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB|
                         RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
	

  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  /* Compute the prescaler value */
  PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 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(TIM3, &TIM_TimeBaseStructure);

  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = TIM3_CCR1_Val;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
  TIM_OC1Init(TIM3, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
	
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = TIM3_CCR2_Val;
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);

  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);

    /* TIM enable counter */
  TIM_Cmd(TIM3, ENABLE);

  /* TIM IT enable */
  TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2 , ENABLE);

}

**频率和占空比的计算:**占空比很简单,每计数16384翻转一下电平,一个周期就要计数32768次,占空比毫无疑问为50%。对于实际的输出频率而言,定时器的时钟是72MHz,然后经过3分频为24MHz,一个周期计数32768次,则实际频率就是24MHz/32768=732.4Hz

uint16_t TIM3_capture = 0;
void TIM3_IRQHandler(void)
{
  /* TIM3_CH1 toggling with frequency = 183.1 Hz */
  if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
    TIM3_capture = TIM_GetCapture1(TIM3);
    TIM_SetCompare1(TIM3, TIM3_capture + TIM3_CCR1_Val );
  }

  /* TIM3_CH2 toggling with frequency = 366.2 Hz */
  if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
    TIM3_capture = TIM_GetCapture2(TIM3);
    TIM_SetCompare2(TIM3, TIM3_capture + TIM3_CCR2_Val);
  }


}
.......
while(1)
{
TIM3_CCR1_Val =2400 ; //TIM3_CH1频率 50KHz 
//三分频 24Mhz / 2400 =10 000 =10Khz = 0.1us 整个周期是0.2us 所以是50Khz
TIM3_CCR2_Val =4800 ; //TIM3_CH1频率 25KHz
//同理上面
}
.......

 可调频率,占空比

前面可调频率,固定占空比的初始化代码一样,主要不同是在中断函数中。通过改变TIMx_CHx_duty来控制占空比,频率通过控制CCR的值

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)
{

  if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    TIM2_capture = TIM_GetCapture2(TIM3);
		if(TIM2_CH2_flag==1)
		{
			TIM2_CH2_flag=0;
      TIM_SetCompare2(TIM2, TIM2_capture +(u16)( TIM2_CH2_duty * TIM2_CCR2_Val));
		}
		else
		{
			TIM2_CH2_flag=1;
			TIM_SetCompare2(TIM2, TIM2_capture +(u16)( TIM2_CCR2_Val*(1-TIM2_CH2_duty)));
    }
  }

  if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    TIM2_capture = TIM_GetCapture3(TIM2);
    TIM_SetCompare3(TIM2, TIM2_capture + TIM2_CCR3_Val);
  }
}

PWM捕获占空比和频率

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

原理:

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

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

PWM方波的占空比应该为TIM3_CH2_ReadValue1*100/TIM3_CH2_ReadValue2

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

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, ENABLE);
}

__IO uint16_t  TIM3_CH2_ReadValue1 = 0, TIM3_CH2_ReadValue2 = 0;
__IO uint16_t  TIM3_CH2_CaptureNumber = 0;
__IO uint32_t  TIM3_CH2_Freq = 0;

__IO uint16_t  TIM3_CH1_ReadValue1 = 0, TIM3_CH1_ReadValue2 = 0;
__IO uint16_t  TIM3_CH1_CaptureNumber = 0;
__IO uint32_t  TIM3_CH1_Freq = 0;

u8 TIM3_CH1_Duty,TIM3_CH2_Duty;
u8 capture_flag = 1;

void TIM3_IRQHandler(void)
{ 
    if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == SET) 
  {
    /* Clear TIM3 Capture compare interrupt pending bit */
    TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
	if(capture_flag == 2)
	{
		if(TIM3_CH2_CaptureNumber == 0)
		{
		  TIM_SetCounter(TIM3, 0);
		  TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Falling);
		  TIM3_CH2_CaptureNumber = 1;
		}
		else if (TIM3_CH2_CaptureNumber == 1)
		{
			/* Get the Input Capture value */
			TIM3_CH2_ReadValue1 = TIM_GetCounter(TIM3);
			TIM_OC2PolarityConfig(TIM3,TIM_ICPolarity_Rising);
			TIM3_CH2_CaptureNumber = 2;
		}
		else if(TIM3_CH2_CaptureNumber == 2)
		{
		  /* Get the Input Capture value */
		  TIM3_CH2_ReadValue2 = TIM_GetCounter(TIM3); 
		  /* Frequency computation */ 
		  TIM3_CH2_Freq = (uint32_t) SystemCoreClock / TIM3_CH2_ReadValue2;
		  TIM3_CH2_Duty = TIM3_CH2_ReadValue1 * 100 / TIM3_CH2_ReadValue2;
		  TIM3_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 == 1)
	{
		if(TIM3_CH1_CaptureNumber == 0)
		{
			TIM_SetCounter(TIM3, 0);
			TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Falling);
			TIM3_CH1_CaptureNumber = 1;
		}
		else if (TIM3_CH1_CaptureNumber == 1)
		{
			 /* Get the Input Capture value */
			TIM3_CH1_ReadValue1 = TIM_GetCounter(TIM3);
			TIM_OC1PolarityConfig(TIM3,TIM_ICPolarity_Rising);
			TIM3_CH1_CaptureNumber = 2;
		}
		else if(TIM3_CH1_CaptureNumber == 2)
		{
		  /* Get the Input Capture value */
		  TIM3_CH1_ReadValue2 = TIM_GetCounter(TIM3); 
		  
		  /* Frequency computation */ 
		  TIM3_CH1_Freq = (uint32_t) SystemCoreClock / TIM3_CH1_ReadValue2;
		  TIM3_CH1_Duty = TIM3_CH1_ReadValue1 * 100 / TIM3_CH1_ReadValue2;
		  TIM3_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)
	{
		sprintf(str,"Number: %d",lcd_number);
		LCD_DisplayStringLine(Line1,str);
		if(key_flag==1)
      {
				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)
					{
						  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

版权声明:本文为CSDN博主「Yinzz2」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_51062259/article/details/122521002

生成海报
点赞 0

Yinzz2

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

SQL585A是一款电子点烟器咪头专用芯片

概述 SQL585A是一款电子点烟器专用芯片。 SQL585A集成 4.0A的放电MOS开关,无需外部MOS,大大降低BOM成本。放电支持 10S超时保护, 以及微秒级快速响应的短路保护,