Advertisement

蓝桥杯嵌入式(F103平台)开发问题记录~持续更新

阅读量:

【LCD】

问题:数据长度变短后,上次的长数据数据依然显示在末尾

解决:液晶一行可正常显示20个ASCII字符,可使用字符串空格的方式避免长数据对短数据的覆盖

复制代码
 LCD_DisplayStringLine(Line0, (unsigned char *)"  hello lanqiao dasai");

    
 // 解决字符串覆盖问题 (加空格)
    
 LCD_DisplayStringLine(Line0, (unsigned char *)"  www.xatu.edu.cn    ");	
    
    
    
    

【LCD】

问题:液晶格式化显示数据

解决:添加头文件"stdio.h",使用sprintf函数,利用%进行格式化输出

复制代码
 // 常用格式化类型:

    
 %d   有符号十进制整数
    
 %u   无符号十进制整数
    
 %x   无符号十六进制数
    
 %o   无符号八进制数
    
 %e   e指数科学计数法
    
 %E   E指数科学计数法
    
 %s   字符串
    
 %c   字符
    
 %f   浮点数,默认显示6位小数
    
 %.nf 浮点数,精确小数位数为n
    
 %#x  无符号十六进制数加前缀0x
    
 %#o  无符号八进制数加前缀0
    
 %%   百分号
    
 %5d  十进制整数,长度为5(右对齐,空格填充)
    
 %05d 十进制整数,长度为5(右对齐,0填充)	   
    
 %-5d 十进制整数,长度为5(左对齐,空格填充)	
    
 %+5d 十进制整数,长度为5(右对齐,空格填充,显示正负符号)
    
    
    
    
复制代码
 u8 display_buf[20];

    
  
    
 // 格式化数据示例
    
 sprintf((char *) display_buf, "%5dHz", 5000);
    
 LCD_DisplayStringLine(Line3, display_buf);
    
 sprintf((char *) display_buf, "%.5f", 3.1415926);
    
 LCD_DisplayStringLine(Line4, display_buf);
    
 sprintf((char *) display_buf, "%#X", 15);
    
 LCD_DisplayStringLine(Line5, display_buf);
    
 sprintf((char *) display_buf, "%o", 15);
    
 LCD_DisplayStringLine(Line6, display_buf);
    
 sprintf((char *) display_buf, "%+5d", -15);
    
 LCD_DisplayStringLine(Line7, display_buf);
    
    
    
    

【LCD】

问题:精确控制字符显示位置

解决:液晶显示屏长320,宽240,共划分为10行,编号110(Line0Line9),每行大小32024像素,每个ASCII字符占1624个像素(Fonts.h中已经定义,支持0x20到0x7E)

每行最多正常显示20个ASCII字符,共划分20列,编号120(019),整屏最多显示200字符

每一行的像素起点位置定义如下(lcd.h中可以找到)

复制代码
 #define Line0          0

    
 #define Line1          24
    
 #define Line2          48
    
 #define Line3          72
    
 #define Line4          96
    
 #define Line5          120
    
 #define Line6          144
    
 #define Line7          168
    
 #define Line8          192
    
 #define Line9          216
    
    
    
    

图1 液晶行列分布示意

可以使用LCD_DisplayChar()函数指定单个字符显示,参数依次为行参数、列像素参数、显示字符
图2 液晶1行的320列像素序号分布

对液晶每一行来说,液晶像素序号从右向左编号,依次为(左)319,318,317......0(右),指定列像素光标位置后,从当前像素位置开始向右写入字符,然后光标向右移动到一个下一像素位置

(可以理解为指定319,从319写到304;指定15,从15写到0),注意的是,如果指定的是0,写完0后,移动的下一位是319;如果指定大于319,比如330,依然会从319开始向右写

1个字符占用16*24个像素位置,先写一行像素的16个像素宽度,然后行像素位置加1,开始写下一行像素,直到24行全部写入,一个字符写入完成。

底层代码如下(在lcd.c中可以找到)

复制代码
 /******************************************************************************* * Function Name  : LCD_DrawChar
    
 * Description    : Draws a character on LCD.
    
 * Input          : - Xpos: the Line where to display the character shape.
    
 *                    This parameter can be one of the following values:
    
 *                       - Linex: where x can be 0..9
    
 *                  - Ypos: start column address.
    
 *                  - c: pointer to the character data.
    
 * Output         : None
    
 * Return         : None
    
 *******************************************************************************/
    
 void LCD_DrawChar(u8 Xpos, u16 Ypos, uc16 *c)
    
 {
    
 	u32 index = 0, i = 0;
    
 	u8 Xaddress = 0;
    
    
    
 	Xaddress = Xpos;
    
 	LCD_SetCursor(Xaddress, Ypos);
    
   
    
 	for(index = 0; index < 24; index++)
    
 	{
    
 		LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
    
 		for(i = 0; i < 16; i++)
    
 		{
    
 			if((c[index] & (1 << i)) == 0x00)
    
 			{
    
 				LCD_WriteRAM(BackColor);
    
 			}
    
 			else
    
 			{
    
 				LCD_WriteRAM(TextColor);
    
 			}
    
 		}
    
 		Xaddress++;
    
 		LCD_SetCursor(Xaddress, Ypos);
    
 	}
    
 }
    
    
    
    

例如,图1中在第1行第7列显示字符‘D’,则行参数为Line0,列像素参数为319-16*6,显示字符为‘D’

复制代码
    LCD_DisplayChar(Line0, 319-16*6, 'D');
    

使用两种指定列像素方式显示一行字符示例:

复制代码
 u8 i = 0;

    
 for(i=0; i<20; i++)
    
 {
    
 	// 第一种显示方法
    
 	LCD_DisplayChar(Line1, 16*(20-i)-1, i+'0');
    
 	// 第二种显示方法
    
 	LCD_DisplayChar(Line2, 319-16*i, i+'0');
    
 }
    
    
    
    

【LCD】

问题:高亮选中参数,或选中参数闪烁

解决:精确位置控制+背景颜色改变(LCD_SetBackColor、LCD_SetTextColor)

例如:在时钟参数调整中,要求在选择时、分、秒,分别高亮显示对应参数,示例程序中使用红色背景高亮显示

复制代码
 u8 upload_time[3] = {0, 1, 0};

    
  
    
 H1 = upload_time[0] / 10;
    
 H2 = upload_time[0] % 10;
    
 M1 = upload_time[1] / 10;
    
 M2 = upload_time[1] % 10;
    
 S1 = upload_time[2] / 10;
    
 S2 = upload_time[2] % 10;
    
  
    
 u8 choose = 2;
    
 u8 i = 6;
    
  
    
 sprintf((char *)display_buf, "      Setting      ");
    
 LCD_DisplayStringLine(Line3, display_buf);
    
  
    
 if(choose == 1)
    
 {
    
 	LCD_SetBackColor(Red);
    
 	LCD_SetTextColor(White);	
    
 }
    
 LCD_DisplayChar(Line5, 319-16*(i+0), H1 + '0');
    
 LCD_DisplayChar(Line5, 319-16*(i+1), H2 + '0');
    
 LCD_SetBackColor(Blue);
    
 LCD_SetTextColor(White);
    
 LCD_DisplayChar(Line5, 319-16*(i+2), '-');
    
 if(choose == 2)
    
 {
    
 	LCD_SetBackColor(Red);
    
 	LCD_SetTextColor(White);		
    
 }
    
 LCD_DisplayChar(Line5, 319-16*(i+3), M1 + '0');
    
 LCD_DisplayChar(Line5, 319-16*(i+4), M2 + '0');
    
 LCD_SetBackColor(Blue);
    
 LCD_SetTextColor(White);
    
  
    
 LCD_DisplayChar(Line5, 319-16*(i+5), '-');
    
  
    
 if(choose == 3)
    
 {
    
 	LCD_SetBackColor(Red);
    
 	LCD_SetTextColor(White);	
    
 }
    
 LCD_DisplayChar(Line5, 319-16*(i+6), S1 + '0');
    
 LCD_DisplayChar(Line5, 319-16*(i+7), S2 + '0');
    
 LCD_SetBackColor(Blue);
    
 LCD_SetTextColor(White);
    
    
    
    

图3 高亮显示选中参数示意

选择参数闪烁的代码参考

复制代码
 u8 upload_time[3] = {0, 0, 0};

    
  
    
 H1 = upload_time[0] / 10;
    
 H2 = upload_time[0] % 10;
    
 M1 = upload_time[1] / 10;
    
 M2 = upload_time[1] % 10;
    
 S1 = upload_time[2] / 10;
    
 S2 = upload_time[2] % 10;
    
  
    
 u8 choose = 1;
    
 u8 i = 6;
    
 u8 LCD_Blink = 0;
    
 extern u8 Flag_200ms;
    
  
    
 if(Flag_200ms)	
    
 {
    
     Flag_200ms = 0;
    
     LCD_Blink = ++LCD_Blink % 2;
    
 }
    
  
    
 sprintf((char *)display_buf, "      Setting      ");
    
 LCD_DisplayStringLine(Line3, display_buf);
    
  
    
 if(choose == 0)
    
 {
    
 	LCD_DisplayChar(Line5, 319-16*(i+0), H1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+1), H2 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+2), '-');	
    
 	LCD_DisplayChar(Line5, 319-16*(i+3), M1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+4), M2 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+5), '-');
    
 	LCD_DisplayChar(Line5, 319-16*(i+6), S1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+7), S2 + '0');	
    
 }
    
 else if(choose == 1)
    
 {
    
 	if(LCD_Blink == 0)
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+0), H1 + '0');
    
 		LCD_DisplayChar(Line5, 319-16*(i+1), H2 + '0');	
    
 	}
    
 	else
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+0), ' ');
    
 		LCD_DisplayChar(Line5, 319-16*(i+1), ' ');
    
 	}
    
 	LCD_DisplayChar(Line5, 319-16*(i+2), '-');	
    
 	LCD_DisplayChar(Line5, 319-16*(i+3), M1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+4), M2 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+5), '-');
    
 	LCD_DisplayChar(Line5, 319-16*(i+6), S1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+7), S2 + '0');	
    
 }
    
 else if(choose == 2)
    
 {
    
 	LCD_DisplayChar(Line5, 319-16*(i+0), H1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+1), H2 + '0');	
    
 	LCD_DisplayChar(Line5, 319-16*(i+2), '-');	
    
 	if(LCD_Blink == 0)
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+3), M1 + '0');
    
 		LCD_DisplayChar(Line5, 319-16*(i+4), M2 + '0');	
    
 	}
    
 	else
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+3), ' ');
    
 		LCD_DisplayChar(Line5, 319-16*(i+4), ' ');
    
 	}
    
 	LCD_DisplayChar(Line5, 319-16*(i+5), '-');
    
 	LCD_DisplayChar(Line5, 319-16*(i+6), S1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+7), S2 + '0');		
    
 }
    
 else if(choose == 3)
    
 {
    
 	LCD_DisplayChar(Line5, 319-16*(i+0), H1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+1), H2 + '0');	
    
 	LCD_DisplayChar(Line5, 319-16*(i+2), '-');
    
 	LCD_DisplayChar(Line5, 319-16*(i+3), M1 + '0');
    
 	LCD_DisplayChar(Line5, 319-16*(i+4), M2 + '0');	
    
 	LCD_DisplayChar(Line5, 319-16*(i+5), '-');
    
 	if(LCD_Blink == 0)
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+6), S1 + '0');
    
 		LCD_DisplayChar(Line5, 319-16*(i+7), S2 + '0');
    
 	}
    
 	else
    
 	{
    
 		LCD_DisplayChar(Line5, 319-16*(i+6), ' ');
    
 		LCD_DisplayChar(Line5, 319-16*(i+7), ' ');	
    
 	}	
    
 }
    
    
    
    

图4 闪烁显示选中参数示意

【LED】

官方参考例程

复制代码
 // 入口参数:ucLed-8位显示控制,1-亮,0-灭

    
 void LED_Disp(unsigned char ucLed)
    
 {
    
     GPIO_Write(GPIOC, ~ucLed << 8);
    
     GPIO_SetBits(GPIOD,GPIO_Pin_2);
    
     GPIO_ResetBits(GPIOD,GPIO_Pin_2);
    
 }
    
    
    
    

官方参考例程采用绝对控制的方法将8位LED灯同时进行控制,取反后左移8位,取反是因为低电平点亮LED灯,左移是因为GPIOC口的高8位控制LED灯,最后高电平打开锁存器,数据流入LED灯接口,再低电平关闭锁存器,新数据不再有效。优势是代码简单,但存在一定的问题,对LED灯进行控制时,任何时候都必须从整体考虑,而不能只操作某一个灯,即操作某一个灯时要想一想其它灯此时的状态如何。同时因为每一次LED灯的控制都操作了整个GPIOC口,并将低8位全部写0了(实际上我们并不想操作低8位),GPIO口的低8位同时也控制LCD液晶显示屏,当程序容量不大时,显示屏刷新速度可以很快,对显示屏没有影响,但程序过大时,显示屏刷新速度明显下降,造成屏幕上可能会有细微斑点出现。例如官方程序的601_071,即第七届预赛题-液位监测告警系统的参考答案,就出现这种情况。

图5 液晶与LED灯控制冲突的效果

改进方法:使用逻辑控制方式,只操作GPIOC的高8位,低8位不管,但随之又出现了一系列的问题

问题:LED不受控制(主要表现为当LED和LCD同时存在时,LED灯可以点亮、熄灭、流水、但无法直接翻转和闪烁,有时所有灯会常亮)

复制代码
 // 打开LED灯(清0点亮)

    
 void LED_On(u16 LED)
    
 {
    
 	GPIOC -> ODR &= ~LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 }
    
  
    
 // 关闭LED灯(置1熄灭)
    
 void LED_Off(u16 LED)
    
 {
    
 	GPIOC -> ODR |=  LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 }
    
  
    
 // 翻转LED灯(异或翻转)
    
 void LED_Toggling(u16 LED)
    
 {
    
 	GPIOC -> ODR ^=  LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 }
    
    
    
    

对8个LED灯进行宏定义

复制代码
 # define LD1 GPIO_Pin_8

    
 # define LD2 GPIO_Pin_9
    
 # define LD3 GPIO_Pin_10
    
 # define LD4 GPIO_Pin_11
    
 # define LD5 GPIO_Pin_12
    
 # define LD6 GPIO_Pin_13
    
 # define LD7 GPIO_Pin_14
    
 # define LD8 GPIO_Pin_15
    
    
    
    

解决:因为LED的控制引脚(PC8PC15经PD2控制的锁存器74HC573后连接LED)与LCD的数据线(PC8PC15作为高8位数据线直接连接液晶)发生了冲突,可以分别从LCD和LED入手进行解决。

方法一:此方法需要修改lcd.c文件。可以在写入液晶数据前先获取GPIOC的输出状态,写入液晶数据完成后,再恢复GPIOC的输出状态。具体做法是:在Keil-MDk工程左侧窗口点击Functions,打开lcd.c文件函数列表,找到LCD_Write开头的三个函数,LCD_WriteRAM、LCD_WriteRAM_Prepare、LCD_WriteReg,在函数体内开始前加上u16 out = GPIOC - > ODR**;** 在函数体内结束时加上GPIOC - > ODR = out**;**

复制代码
 void LCD_WriteRAM_Prepare(void)

    
 { 
    
 	u16 out = GPIOC->ODR;
    
 	GPIOB->BRR = 0x0200;  
    
 	GPIOB->BRR = 0x0100; 
    
 	GPIOB->BSRR = 0x0020; 
    
  
    
 	GPIOC->ODR = R34;     
    
 	GPIOB->BRR = 0x0020;   
    
 	GPIOB->BSRR = 0x0020;
    
 	GPIOB->BSRR = 0x0100; 
    
  
    
 	GPIOB->BSRR = 0x0200; 
    
 	GPIOC->ODR = out;
    
 }
    
    
    
    

方法二:此方法不需要修改lcd.c文件。LED灯不受控制的根本原因在于液晶写入数据时会修改PC8PC15的值,导致PC8PC15的引脚状态发生改变,所以可以在控制完LED后,保存当前LED灯状态,下次控制LED灯的开始,再加载上次的状态,这样即使液晶改变了引脚状态,但是因为有上次LED状态的备份,仍然可以通过与、或、非逻辑来单独操作某一LED灯,而不影响其他灯的状态。上电默认LED为全灭,LED引脚状态初始值可设置为0xFF00。主要添加语句为GPIOC -> ODR = status; status = GPIOC -> ODR;

复制代码
 u16 status = 0xFF00;

    
  
    
 void LED_Init(void)
    
 {
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
    
   
    
 	GPIO_InitStructure.GPIO_Pin = 0xFF00;
    
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
    
  
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
    
  
    
 	status = GPIOC -> ODR;
    
 	LED_Off(0xFF00);	
    
 }
    
  
    
 // 打开LED灯(清0点亮)
    
 void LED_On(u16 LED)
    
 {
    
 	GPIOC -> ODR = status;
    
 	GPIOC -> ODR &= ~LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 	status = GPIOC -> ODR;
    
 }
    
  
    
 // 关闭LED灯(置1熄灭)
    
 void LED_Off(u16 LED)
    
 {
    
 	GPIOC -> ODR = status;
    
 	GPIOC -> ODR |=  LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 	status = GPIOC -> ODR;
    
 }
    
  
    
 // 翻转LED灯(取反翻转)
    
 void LED_Toggling(u16 LED)
    
 {
    
 	GPIOC -> ODR = status;
    
 	GPIOC -> ODR ^=  LED;
    
 	GPIOD -> ODR |=  (1<<2);
    
 	GPIOD -> ODR &= ~(1<<2);
    
 	status = GPIOC -> ODR;
    
 }
    
    
    
    

【TIM】

问题:定时不正确

解决:确保复制后的程序TIMx标号修改正确,建议使用TIM4作为程序定时器,也可以使用滴答定时器SysTick和高级定时器TIM1。TIM2和TIM3一般用于PWM输出或捕获(竞赛开发板上留有引脚接口PA1、PA2、PA6、PA7),不建议作为程序定时器。

复制的代码为TIM2,需要将所有TIM2替换为TIM4,可使用Ctrl+H批量替换,中断触发方式修改为TIM_IT_Update,定时1ms的参数设置为预分频系数为TIM_Prescaler=72-1,周期为TIM_Period=1000-1

因默认的定时器时钟TIMx_CLK为72MHz,则定时器的计数器时钟为CNT_CLK = 72MHz/72 = 1MHz,又周期为1000-1,则定时器触发一次中断的时间为1/CNT_CLK*(TIM_Period+1)=1/1MHz * 1000 = 1us*1000=1ms

为避免程序定时器中断对其它程序产生干扰或不断陷入中断导致程序僵死,中断函数执行时间应尽可能短,因此函数体内不应放太多的程序语句或运算量复杂的表达式,可以设置事件标志位,并在main函数中的while(1)内根据标志位的变化进行相应事件的处理

复制代码
 u8 flag_10ms = 0;

    
 u8 flag_100ms = 0;
    
 u8 flag_500ms = 0;
    
  
    
 void TIM4_Init(void)
    
 {
    
 	NVIC_InitTypeDef NVIC_InitStructure;
    
 	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    
  
    
 	/* TIM4 clock enable */
    
 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    
 	
    
 	/* Enable the TIM4 global Interrupt */
    
 	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);
    
 	/* Time base configuration */
    
 	TIM_TimeBaseStructure.TIM_Period = 1000-1;
    
 	TIM_TimeBaseStructure.TIM_Prescaler = 72-1;
    
 	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    
 	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    
 	
    
 	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
    
 	/* TIM IT enable */
    
 	TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    
 	
    
 	/* TIM4 enable counter */
    
 	TIM_Cmd(TIM4, ENABLE);	
    
 }
    
  
    
 void TIM4_IRQHandler(void)
    
 {
    
 	static u16 cnt1 = 0;
    
 	static u16 cnt2 = 0;
    
 	static u16 cnt3 = 0;
    
 	if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    
 	{
    
 		TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    
 		if(++cnt1 >= 10){cnt1 = 0; flag_10ms = 1;}
    
 		if(++cnt2 >= 100){cnt2 = 0; flag_100ms = 1;}
    
 		if(++cnt3 >= 500){cnt3 = 0; flag_500ms = 1;}
    
 	
    
 	}
    
 }
    
    
    
    

若使用系统滴答定时器SysTick,则无需开启定时器TIM44

液晶参考工程示例main.c中的代码已经设置滴答定时器为1ms中断,如下

复制代码
    SysTick_Config(SystemCoreClock/1000);
    

只需找到stm32f10x_it.c中的SysTick_Handler中断函数,编写参考代码如下:

复制代码
 u8 Flag_10ms = 0;

    
 u8 Flag_100ms = 0;
    
 u8 Flag_200ms = 0;
    
 void SysTick_Handler(void)
    
 {
    
 	static u16 cnt1 = 0; 
    
 	static u16 cnt2 = 0; 
    
 	static u16 cnt3 = 0; 
    
 	if(++cnt1>=10){cnt1=0;Flag_10ms=1;}
    
 	if(++cnt2>=100){cnt2=0;Flag_100ms=1;}
    
 	if(++cnt3>=200){cnt3=0;Flag_200ms=1;}	
    
 	TimingDelay--;
    
 }
    
    
    
    

【KEY】

问题:按键无反应

解决:初始化按键时,确保GPIO口设置为输入浮空状态GPIO_Mode_IN_FLOATING,在头文件中对4个独立按键的宏定义确保为读取输入位状态GPIO_ReadInputDataBit

复制代码
 void KEY_Init(void)

    
 {
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
 	
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_8;
    
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    
 	GPIO_Init(GPIOA, &GPIO_InitStructure);	
    
  
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
    
 	GPIO_Init(GPIOB, &GPIO_InitStructure);	
    
 }
    
    
    
    

对4个独立按键的电平进行宏定义

复制代码
 #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
    
    
    
    

【EEPROM】

问题:读取数据始终为255

解决:首先确保读取地址是正确的,而且读取地址里的数据已经写入已知的数据,很有可能是写入失败,因为AT24C02中默认的数据就是1,一个字节就是FF,也就是255

I2CStart()、I2CStop()后都没有等待应答的语句,读数据会先发送写命令(0xa0),然后发送写数据地址(一种假写方式,目的是将地址指针移到目的地址),再重启一次连接,然后读取一字节,最后发送非应答(实际测试发现:发送应答或等待应答这两条语句都可以正常读取)


AT24C02属于2K大小,其地址A2 A1 A0(原理图上表述为E0 E1 E2)在硬件上已经接地,R/W为0表示写,为1表示读,因此0xa0(10100000)表示写操作,0xa1(10100001)表示读操作

复制代码
 u8 Read_AT24C02(u8 add)

    
 {
    
 	u8 temp = 0;
    
 	I2CStart();
    
 	I2CSendByte(0xa0);	
    
 	I2CWaitAck();
    
 	I2CSendByte(add);
    
 	I2CWaitAck();
    
 	I2CStart();
    
 	I2CSendByte(0xa1);	
    
 	I2CWaitAck();
    
 	temp = I2CReceiveByte();
    
 	I2CSendNotAck();
    
 	I2CStop();	
    
 	return temp;	
    
 }
    
    
    
    

【EEPROM】

问题:写入新数据后,读取出的仍然是原数据

解决:检测写入函数是否正确,同时两次写入需要间隔5ms的时间,Delay_Ms(5)

复制代码
 void Write_AT24C02(u8 add, u8 dat)

    
 {
    
 	I2CStart();
    
 	I2CSendByte(0xa0);	
    
 	I2CWaitAck();
    
 	I2CSendByte(add);
    
 	I2CWaitAck();
    
 	I2CSendByte(dat);
    
 	I2CWaitAck();
    
 	I2CStop();	
    
 	delay1(500);	
    
 }
    
    
    
    

写入数据时需要间隔5ms


复制代码
 Write_AT24C02(0x00, 40);

    
 Delay_Ms(5);
    
 Write_AT24C02(0x01, 50);
    
    
    
    

【EEPROM】

问题:判断是否首次上电,即系统首次上电采用默认参数配置,向EEPROM中保存数据后,下次上电就直接读取EEPROM中的数据

解决:指定一个EEPROM地址,上电后从此位置读取数据,若读取到的数据符合指定内容,就认为系统已经向EEPROM中保存过数据,因此从指定位置读取已保存的参数,否则判定为首次上电,使用默认的参数

本程序指定0xFF地址,指定内容为‘#’,地址和内容可自己灵活设置(不要设置成255,因为EEPROM默认的数据就是255)

复制代码
 void Load_Data(void)

    
 {
    
 	if(Read_AT24C02(0xFF) == '#')
    
 	{
    
 		u8 i = 0;
    
 		for(i=0; i<sizeof(float); i++)
    
 		{
    
 			float_read.b[i] = Read_AT24C02(0x00+i);	
    
 		}
    
 		k = float_read.a;	
    
 	}		
    
 }
    
    
    
    

当保存参数数据时,在指定EEPROM地址写入保存标志内容

复制代码
 float_write.a = k;

    
 for(i=0; i<sizeof(float); i++)
    
 {
    
 	Write_AT24C02(0x00+i, float_write.b[i]);
    
 	Delay_Ms(5);
    
 }	
    
 Write_AT24C02(0xFF, '#');
    
 Delay_Ms(5);
    
    
    
    

【ADC】

问题:ADC读取数据不正常

解决:使能ADC1时钟,GPIOB时钟,ADC时钟分频系数为6,GPIO引脚为PB.0,IO引脚模式设置为GPIO_Mode_AIN

STM32F103 默认系统时钟为72MHz(最高时钟频率),APB2时钟默认为72MHz,ADC的时钟来自APB2时钟的分频,但不超过14MHz

对APB2时钟进行6分频后为72 / 6 = 12MHz,满足要求,即程序中设置f_ADCCLK = 12MHz

规则通道配置为外部通道8,连接PB0(左下角的R37电位器),采样时间参数设置为1.5,采样时间为(1.5+12.5)/12 = 1.167us

注入通道配置为内部通道16,连接内部温度传感器,采样时间参数设置为239.5,采样时间为(239.5+12.5)/12 = 21us >17.1us

复制代码
 void ADC1_Init(void)

    
 {
    
 	ADC_InitTypeDef ADC_InitStructure;
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	
    
     /* Enable ADC1 and GPIOB clock */
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB, ENABLE);
    
 	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
 	
    
 	/* Configure PB.0 (ADC Channel8) as analog input -------------------------*/
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
    
 	
    
 	/* ADC1 configuration ------------------------------------------------------*/
    
 	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    
 	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    
 	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    
 	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    
 	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    
 	ADC_InitStructure.ADC_NbrOfChannel = 1;
    
 	ADC_Init(ADC1, &ADC_InitStructure);
    
 	
    
 	/* ADC1 regular channel8 configuration */ 
    
 	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_1Cycles5);
    
 	
    
 	/* ADC1 injected channel16 Configuration */ 
    
 	ADC_InjectedChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);
    
 	ADC_TempSensorVrefintCmd(ENABLE);
    
 	ADC_AutoInjectedConvCmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 DMA */
    
 	ADC_DMACmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 */
    
 	ADC_Cmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 reset calibration register */   
    
 	ADC_ResetCalibration(ADC1);
    
 	/* Check the end of ADC1 reset calibration register */
    
 	while(ADC_GetResetCalibrationStatus(ADC1));
    
 	
    
 	/* Start ADC1 calibration */
    
 	ADC_StartCalibration(ADC1);
    
 	/* Check the end of ADC1 calibration */
    
 	while(ADC_GetCalibrationStatus(ADC1));
    
 	 
    
 	/* Start ADC1 Software Conversion */ 
    
 	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	
    
 }
    
    
    
    

温度读取

复制代码
 u16 ADC1_Conv(void)

    
 {
    
 	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
 	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));	
    
 	return ADC_GetConversionValue(ADC1);
    
 }
    
  
    
 u16 ADC1_InjeConv(void)
    
 {
    
 	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
 	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC));	
    
 	ADC_ClearFlag(ADC1, ADC_FLAG_JEOC);
    
 	return ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
    
 }
    
    
    
    
复制代码
 temp_value = ADC1_InjeConv();

    
 sprintf((char *)display_buf, "temp:%5.2f", (float)25+(1.43-temp_value*3.3/4095)/0.0043);
    
 LCD_DisplayStringLine(Line5, display_buf);
    
  
    
 adc_value = ADC1_Conv();
    
 sprintf((char *)display_buf, "volt:%.2f V", adc_value*3.3/4095);
    
 LCD_DisplayStringLine(Line6, display_buf);
    
    
    
    

【ADC】

问题:使用DMA方式采集AD值

复制代码
 #define ADC1_DR_Address    ((uint32_t)0x4001244C)

    
 u16 ADCConvertedValue[10];
    
  
    
 void ADC1_Init(void)
    
 {
    
 	ADC_InitTypeDef ADC_InitStructure;
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	DMA_InitTypeDef DMA_InitStructure;
    
 	/* Enable ADC1 and GPIOB clock */
    
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB, ENABLE);
    
 	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
 	
    
 	/* Configure PB.0 (ADC Channel8) as analog input -------------------------*/
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
    
 	
    
 	/* DMA1 channel1 configuration ----------------------------------------------*/
    
 	DMA_DeInit(DMA1_Channel1);
    
 	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    
 	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
    
 	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    
 	DMA_InitStructure.DMA_BufferSize = 1;
    
 	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    
 	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    
 	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    
 	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    
 	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    
 	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    
 	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    
 	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
 	
    
 	/* Enable DMA1 channel1 */
    
 	DMA_Cmd(DMA1_Channel1, ENABLE);
    
 	
    
 	/* ADC1 configuration ------------------------------------------------------*/
    
 	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    
 	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    
 	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    
 	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    
 	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    
 	ADC_InitStructure.ADC_NbrOfChannel = 1;
    
 	ADC_Init(ADC1, &ADC_InitStructure);
    
 	
    
 	/* ADC1 regular channel8 configuration */ 
    
 	ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_1Cycles5);
    
 	
    
 	/* ADC1 injected channel16 Configuration */ 
    
 	ADC_InjectedChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);
    
 	ADC_TempSensorVrefintCmd(ENABLE);
    
 	ADC_AutoInjectedConvCmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 DMA */
    
 	ADC_DMACmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 */
    
 	ADC_Cmd(ADC1, ENABLE);
    
 	
    
 	/* Enable ADC1 reset calibration register */   
    
 	ADC_ResetCalibration(ADC1);
    
 	/* Check the end of ADC1 reset calibration register */
    
 	while(ADC_GetResetCalibrationStatus(ADC1));
    
 	
    
 	/* Start ADC1 calibration */
    
 	ADC_StartCalibration(ADC1);
    
 	/* Check the end of ADC1 calibration */
    
 	while(ADC_GetCalibrationStatus(ADC1));
    
 	 
    
 	/* Start ADC1 Software Conversion */ 
    
 	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	
    
 }
    
    
    
    

主函数直接读取

复制代码
 extern u16 ADCConvertedValue[10];

    
  
    
 void adc_dma_process(void)
    
 {
    
 	u8 i = 0;
    
 	u32 value = 0;
    
 	for(i=0; i<20; i++)
    
 	{
    
 		value =  value + ADCConvertedValue[0];
    
 	}
    
 	value = value / 20;
    
 	sprintf((char *)display_buf, "volt:%.2fV", value*3.3/4095);
    
 	LCD_DisplayStringLine(Line0, display_buf);	
    
 }
    
    
    
    

【USART】

问题:接收不定长数据出错

解决:将发送寄存器空中断替换为空闲中断IDLE

复制代码
 void USART2_Init(u32 BaudRate)

    
 {
    
 	NVIC_InitTypeDef NVIC_InitStructure;
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	USART_InitTypeDef USART_InitStructure;
    
 	
    
 	/* Enable GPIO clock */
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); 
    
 	
    
 	/* Configure the NVIC Preemption Priority Bits */  
    
 	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    
 	/* Enable the USART2 Interrupt */
    
 	NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    
 	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    
 	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    
 	NVIC_Init(&NVIC_InitStructure);
    
 	
    
 	/* Configure USART2 Tx as alternate function push-pull */
    
 	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);
    
 	
    
 	/* Configure USART2 Rx as input floating */
    
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
 	
    
 	USART_InitStructure.USART_BaudRate = BaudRate;
    
 	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;
    
 	
    
 	/* Configure USART2 */
    
 	USART_Init(USART2, &USART_InitStructure);
    
 	
    
 	/* Enable USART2 Receive and Transmit interrupts */
    
 	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
    
 	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
    
 	
    
 	/* Enable the USART2 */
    
 	USART_Cmd(USART2, ENABLE);
    
 }
    
    
    
    

中断处理函数

复制代码
 void USART2_IRQHandler(void)

    
 {
    
 	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
    
 	{
    
 		/* Read one byte from the receive data register */
    
 		RxBuffer[RxCounter++] = USART_ReceiveData(USART2);
    
 	}
    
 	
    
 	else if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
    
 	{   
    
 		USART2 -> SR; 
    
 		USART2 -> DR; 
    
 		Flag_Rx = 1;
    
 	}
    
 }
    
    
    
    

【USART】

问题:无法使用printf函数,格式化串口发送数据

解决:重写fputc函数,若没有添加头文件"stdio.h",内参为char *f,若添加了头文件"stdio.h",内参为FILE *f

通过Target Options的Target选项卡开启MicroLIB

替换发送移位寄存器空(发送完成)标志位TC为发送数据寄存器空标志位TXE

复制代码
 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;
    
 }
    
    
    
    

【RTC】

问题:烧录axf程序后液晶显示屏左4/5出现白屏

解决:注释RTC_Configuration()函数中最后一行BKP_RTCOutputConfig(BKP_RTCOutputSource_Second)

复制代码
 void RTC_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);
    
 	
    
 	/* 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);
    
 }
    
    
    
    

【RTC】

问题:无法显示00:00:00时间

解决:修改重置时间参数为0x00015180,增加一句TimeVar = 0;

复制代码
 void Time_Display(uint32_t TimeVar)

    
 {
    
 	/* Reset RTC Counter when Time is 20:00:00 */
    
 	if (RTC_GetCounter() == 0x00015180)
    
 	{
    
 		RTC_SetCounter(0x0);
    
 		TimeVar = 0;
    
 		/* 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;
    
 	//printf("Time: %0.2d:%0.2d:%0.2d\r", THH, TMM, TSS);
    
 }
    
    
    
    

【RTC】

问题:经过00:00:00时会停留2s才变化到00:00:01

解决:时间更新部分与显示部分分开,即执行完Time_Display(RTC_GetCounter())后,不要立即显示时间

复制代码
  while(1)

    
 	{
    
 		if(TimeDisplay)
    
 		{
    
 			TimeDisplay = 0;
    
 			Time_Display(RTC_GetCounter());	
    
 		}	
    
 		if(flag_500ms)
    
 		{
    
 			flag_500ms = 0;
    
 			LED_Toggling(LD1);
    
 		}
    
 		if(flag_10ms)
    
 		{
    
 			flag_10ms = 0;
    
 			KEY_Process();
    
 		}
    
 		sprintf((char*)display_buf, "Time: %0.2d:%0.2d:%0.2d",THH,TMM,TSS);	
    
 		LCD_DisplayStringLine(Line3, display_buf);	
    
 	}
    
    
    
    

【PWM】

1、输出比较

复制代码
 u32 CNT_Clock = 1*1000*1000;

    
 uint16_t PrescalerValue = 0;
    
  
    
 void TIM3_PWM_OCToggle(void)
    
 {
    
 	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    
 	TIM_OCInitTypeDef  TIM_OCInitStructure;
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	NVIC_InitTypeDef NVIC_InitStructure;
    
  
    
 	/* TIM3 clock enable */
    
 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    
 	
    
 	/* GPIOA clock enable */
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| 
    
 							RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
    
 	
    
 	/* GPIOA Configuration:TIM3 Channel1, 2, 3 and 4 as alternate function push-pull */
    
 	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);
    
  
    
 	/* Enable the TIM3 global Interrupt */
    
 	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);
    
  
    
 	/* Compute the prescaler value */
    
 	PrescalerValue = (uint16_t) (SystemCoreClock / CNT_Clock) - 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);
    
 	
    
 	/* Output Compare Toggle Mode configuration: Channel1 */
    
 	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    
 	TIM_OCInitStructure.TIM_Pulse = 0;
    
 	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    
 	TIM_OC1Init(TIM3, &TIM_OCInitStructure);
    
 	
    
 	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
    
 	
    
 	/* Output Compare Toggle Mode configuration: Channel2 */
    
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    
 	TIM_OCInitStructure.TIM_Pulse = 0;
    
 	
    
 	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);
    
 }
    
    
    
    

使用IO模拟的方式(定时器输出比较翻转)

设置通道CHx输出PWM波

复制代码
 u32 CNT_Clock = 1*1000*1000;

    
  
    
 __IO uint16_t TIM3_CCR1_Val = 0;
    
 __IO float TIM3_CH1_Duty = 0;
    
  
    
 __IO uint16_t TIM3_CH1_Hight = 0;
    
 __IO uint16_t TIM3_CH1_Low = 0;
    
  
    
  
    
 void TIM3_PWM_CH1(u32 freq, float duty)
    
 {
    
 	TIM3_CCR1_Val = CNT_Clock / freq;
    
 	TIM3_CH1_Duty = duty;	
    
 	TIM3_CH1_Hight = TIM3_CCR1_Val * TIM3_CH1_Duty;
    
 	TIM3_CH1_Low = TIM3_CCR1_Val * (1 - TIM3_CH1_Duty);
    
 }
    
    
    
    

在中断中翻转电平极性

复制代码
 void TIM3_IRQHandler(void)

    
 {
    
 	static u8 flag_CH1 = 0; 
    
 	static u8 flag_CH2 = 0;
    
 	u16 capture = 0;
    
 	if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
    
 	{
    
 		TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
    
 		capture = TIM_GetCapture1(TIM3);
    
 		if(flag_CH1 == 0)
    
 		{
    
 			flag_CH1 = 1;
    
 			TIM_SetCompare1(TIM3, capture + TIM3_CH1_Hight);
    
 		}
    
 		else
    
 		{
    
 			flag_CH1 = 0;
    
 			TIM_SetCompare1(TIM3, capture + TIM3_CH1_Low);	
    
 		}	
    
 	}
    
 	
    
 	else if(TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)
    
 	{
    
 		TIM_ClearITPendingBit(TIM3, TIM_IT_CC2);
    
 		capture = TIM_GetCapture2(TIM3);
    
 		if(flag_CH2 == 0)
    
 		{
    
 			flag_CH2 = 1;
    
 			TIM_SetCompare2(TIM3, capture + TIM3_CH2_Hight);
    
 		}
    
 		else
    
 		{
    
 			flag_CH2 = 0;
    
 			TIM_SetCompare2(TIM3, capture + TIM3_CH2_Low);	
    
 		}	
    
 	}
    
 }
    
    
    
    

输入捕获

复制代码
 void TIM2_PWM_Capture(void)

    
 {
    
 	TIM_ICInitTypeDef  TIM_ICInitStructure;
    
 	GPIO_InitTypeDef GPIO_InitStructure;
    
 	NVIC_InitTypeDef NVIC_InitStructure;
    
  
    
 	/* TIM2 clock enable */
    
 	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    
 	
    
 	/* GPIOA and GPIOB clock enable */
    
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
  
    
 	/* TIM2 channel 2
    
 	 pin (PA.01, PA.02) configuration */
    
 	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1 | GPIO_Pin_2;
    
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
  
    
 	/* 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);
    
  
    
 	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(TIM2, &TIM_ICInitStructure);
    
  
    
 	TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;
    
 	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(TIM2, &TIM_ICInitStructure);
    
 	
    
 	/* TIM enable counter */
    
 	TIM_Cmd(TIM2, ENABLE);
    
 	
    
 	/* Enable the CC2 Interrupt Request */
    
 	TIM_ITConfig(TIM2, TIM_IT_CC2 | TIM_IT_CC3, ENABLE);
    
 }
    
    
    
    

通道CHx进行捕获

复制代码
 u32 TIM2_CH2_Freq = 0;

    
 float TIM2_CH2_Duty = 0;
    
  
    
 u8 flag_Capture = 2;
    
 u8 CaptureNumber = 0;
    
 u16 ReadValue1 = 0;
    
 u16 ReadValue2 = 0;
    
  
    
 void TIM2_CH2_Capture(void)
    
 {
    
 	if(flag_Capture == 2)
    
 	{
    
 		if(CaptureNumber == 0)
    
 		{
    
 			TIM_SetCounter(TIM2, 0);
    
 			TIM_OC2PolarityConfig(TIM2, TIM_ICPolarity_Falling);
    
 			CaptureNumber = 1;
    
 		}
    
 		else if(CaptureNumber == 1)
    
 		{
    
 			/* Get the Input Capture value */
    
 			ReadValue1 = TIM_GetCounter(TIM2); 
    
 			TIM_OC2PolarityConfig(TIM2, TIM_ICPolarity_Rising);
    
 			CaptureNumber = 2;
    
 		}
    
 		else if(CaptureNumber == 2)
    
 		{
    
 			/* Get the Input Capture value */
    
 			ReadValue2 = TIM_GetCounter(TIM2); 	
    
 			if (ReadValue2 > ReadValue1)
    
 			{
    
 				/* Frequency computation */ 
    
 				TIM2_CH2_Freq = (uint32_t) SystemCoreClock / ReadValue2;
    
 				TIM2_CH2_Duty = ReadValue1 * 1.0 / ReadValue2;
    
 			}
    
 			else
    
 			{
    
 				/* Frequency computation */ 
    
 				TIM2_CH2_Freq = (uint32_t) SystemCoreClock / (ReadValue2 + 0xFFFF);
    
 				TIM2_CH2_Duty = ReadValue1 * 1.0 / (ReadValue2 + 0xFFFF);
    
 			}
    
 			CaptureNumber = 0;
    
 		}
    
 	}	
    
 }
    
    
    
    

在中断中进行捕获

复制代码
 void TIM2_IRQHandler(void)

    
 { 
    
 	if(TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET) 
    
 	{
    
 		/* Clear TIM2 Capture compare interrupt pending bit */
    
 		TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
    
 		TIM2_CH2_Capture();	
    
 	}
    
 	else if(TIM_GetITStatus(TIM2, TIM_IT_CC3) == SET) 
    
 	{
    
 		/* Clear TIM2 Capture compare interrupt pending bit */
    
 		TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
    
 		TIM2_CH3_Capture();	
    
 	}
    
 }
    
    
    
    

主函数中使用时分复用的方式切换捕获pwm的通道

复制代码
 extern u8 flag_Capture;

    
 extern u8 CaptureNumber;
    
  
    
 u32 PA6_PWM_Freq = 2*1000;
    
 float PA6_PWM_Duty = 0.4; 
    
 u32 PA7_PWM_Freq = 1*1000;
    
 float PA7_PWM_Duty = 0.6; 
    
  
    
 int main(void)
    
 {
    
 	LCD_Init();	
    
 	LED_Init();
    
 	TIM4_Init();
    
 	KEY_Init();
    
 	TIM3_PWM_OCToggle();
    
 	TIM2_PWM_Capture();
    
 	TIM3_PWM_CH1(PA6_PWM_Freq, PA6_PWM_Duty);
    
 	TIM3_PWM_CH2(PA7_PWM_Freq, PA7_PWM_Duty);
    
 	while(1)
    
 	{
    
 		if(flag_500ms)
    
 		{
    
 			flag_500ms = 0;
    
 			LED_Toggling(LD1);
    
 		}
    
 		if(flag_100ms)
    
 		{
    
 			flag_100ms = 0;
    
 			flag_Capture = ++flag_Capture % 4;
    
 			CaptureNumber = 0;
    
 		}
    
 		if(flag_10ms)
    
 		{
    
 			flag_10ms = 0;
    
 			KEY_Process();
    
 		}
    
 		LCD_Update();
    
 	}
    
 }
    
    
    
    

全部评论 (0)

还没有任何评论哟~