【蓝桥杯嵌入式组】底层易错总结

底层代码参考建Gihub<传送门>

模块 模式
LED 普通推挽输出(GPIO_Mode_Out_PP)
KEY 浮空输入(GPIO_Mode_IN_FLOATING)
BEEP 普通推挽输出(GPIO_Mode_Out_PP)
ADC 模拟输入(GPIO_Mode_AIN)
USART2_RX 浮空输入(GPIO_Mode_IN_FLOATING)
USART2_TX 复用推挽输出(GPIO_Mode_AF_PP)
PWM输出 复用推挽输出(GPIO_Mode_AF_PP)
PWM捕获 浮空输入(GPIO_Mode_IN_FLOATING)

1、移植LCD前先看看,能不能直接下载到开发板,如果可以,直接用,不必再去费事移植。

2、工程可以使用后,打开数据手册和原理图。【备查引脚使用】

3、写定时器4底层

  • 参考固件库中的\Project\STM32F10x_StdPeriph_Examples\TIM\TimeBase
  • 配置好相应的NVIC和TIM,注意前提打开时钟
  • 最后开启定时器并且开启定时器更新中断【不会写的变量名直接索引到底层查看】
  • 写中断函数【不会的函数名,可在启动文件中索引】,以及上述例程的中段文件
  • 注意中断函数名字是TIM4_IRQHandler,不是TIM4_Handler
  • 最后在主函数中调用TIM4_Init(2000, 72);//72分频,计数值2000 -- 2ms定时即可。【注意参数位置和具体大小】
  • 复制的时候竟然忘了改定时器名字,造成分频和周期值没有装进去,定时特别快!

4、写LED底层

  • 依旧可以参考上述定时器的那个例程
  • 配置好时钟和GPIO【注意PD2是锁存器选通引脚,PC8~PC15是LED对应管脚,全都配置成强推挽输出,最后初始化定时器初始状态为关闭】
	GPIOD->ODR |= (1<<2);
	GPIOC->ODR = 0xFFFF;//小灯初始状态关闭
	GPIOD->ODR &=~(1<<2);
  • 在主函数中用Delay_Ms测试闪烁
  • 无误后用LED测试定时器4

5、写KEY底层

  • GPIO配置可复制LED底层
  • 配置好四个引脚PA0、PA8、PB1、PB2【注意的是这几个脚都是浮空输入】
  • 注意别忘了【打开时钟】
  • STM32的按键配置需要初始化GPIO,并且在主函数中调用也别忘了。
  • 书写的时候直接也一并把长按键也写上【长按键的KeyDown初始化为0】
  • 别忘了在.h总直接宏定义KEY1~KEY4,用GPIO_ReadInputDataBit函数
  • 最后需要在主函数初始化KEY,并在while1中调用KeyDriver,并在中断中按键扫描
  • 易错的地方:忘记重新更新backup

6、写BEEP底层

  • 跟LED大部分一样,可直接参考LED部分
  • 需要主要的是PB4是JTAG的JTRST引脚,作为普通IO的话需要将其禁用,禁用方法就时调用GPIO_PinRemapConfig,对了别忘了开启AFIO时钟!
  • 务必先开启AFIO时钟,然后再GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);
  • 然后再编写独有的BeepScan(…)和Beep(…)函数
  • 全局变量BeepTimer是个有符号32变量
  • 最后可通过按键测试

7、写EEPROM底层

  • 不需要再写I2C底层,可直接从对应的资料中嵌入式设计与开发\I2C参考程序找到i2c对应底层,并复制到相应位置。
  • 写的过程“启动->写入写操作指令0xA0->等待回应->写入存储地址->等待回应->写入数据->等待回应->停止”
  • 读的过程“启动->写入写操作指令0xA0->等待回应->写入存储地址->等待回应 // 重新启动->写入读操作指令0xA1->等待回应->读取数据->等待回应->停止”
  • 需要在主函数中初始化i2c_init()
  • 使用液晶显示E2中的数据,需要用到sprintf,加入stdio.h头文件

8、写ADC底层

  • 可参考之前写的相关配置以及ADC\ADC1_DMA例程配置
  • 需要使能GPIOB(配置为模拟输入模式)和ADC1时钟
  • ADC1时钟需要进行6分频
  • 同时需要把ADC_ScanConvModeADC_ContinuousConvMode改为DISABLE,表示单通道和单次转换
  • 最后使能ADC1
  • 启动ADC校准(四行代码)
  • 读取ADC时候,配置ADC_RegularChannelConfig为通道8,且为239.5转换周期->启动软件触发->等待EOC标志位(可到adc底层文件看相关变量)->获取ADC转换值->关闭软件触发
  • 注意等待ECO的标志位是:while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == 0); 日哦
  • 注意ADC初始化命名的时候不能那个叫ADC_Init(void),底层函数有一个函数是叫这个了,这里起名为void ADC1_Init()
  • 等待转换完成是FLAG标志位不是IT标志位while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == 0);

9、写RTC底层

  • 可参考固件库中的\RTC\Calendar例程
  • 需要修好的是LSI时钟部分,使能LSI时钟RCC_LSICmd(ENABLE);,接下来是等待使能完毕
  • 最后要初始化RTC_SetCounter,然后RTC_WaitForLastTask
  • 秒转换为对应的单位时,关系如下
	HH = time / 3600;
	MM = time % 3600 / 60;
	SS = time % 3600 % 60;
  • 24:00:00特殊处理下
	if(time == (23*3600 + 59*60 + 59))
	{
		RTC_SetCounter(0);
	}
	
	HH = time / 3600;
	MM = time % 3600 / 60;
	SS = time % 3600 % 60;
	
	sprintf((char*)str, "RTC: %.2d:%.2d:%.2d ", HH, MM, SS);
	LCD_DisplayStringLine(Line6, str);

10、写USART2底层

  • 参考USART\DMA_Interrupt例程
  • 别忘了先开启对应的GPIO和USART的时钟
  • 接下来配置TX为复用推挽输出,RX为浮空输入,然后配置中断向量
  • 接着配置USART对应的结构体成员
  • 最后启动USART,并开启串口接收中断
  • 串口发送中断采用查询的方式,所以接收对应标志位是IT相关,而发送对应的标志位是FLAG相关的,要特别注意!
  • 在接收中断中,一旦判断到结束条件就要关闭接收中断;在主函数中进行数据处理完毕后再打开接收中断,记得清零对应的接收数组
  • 发送数据的时候不需要打开中断,而是采用查询的方式while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == 0);
  • 还需要注意TXD配置为复用推挽输出
	for(i=0; i<20; i++)
	{
		RxdBuf[i] = 0;
	}
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

11、写PWM模式底层

  • 参考\TIM\7PWM_Output例程
  • 使能GPIO时钟和TIM时钟
  • 注意GPIO此时应该是强推挽输出
  • 形参传入freqduty
  • freq算周期Period的方法为:1000000/freq,分频系数为72
  • 占空比由Pulse控制:(arr-1) * duty / 100;
  • 看清楚是哪个定时器输出,配置好定时器的周期分频值。
  • 看清楚是定时器的第几个通道,相应的TIM_OCxInit中的x就是几
  • TIM_OCMode和TIM_OCPolarity分别对应关系是:TIM_OCMode_PWM2-TIM_OCPolarity_Low,TIM_OCMode_PWM1-TIM_OCPolarity_High

12、写输出比较产生PWM底层

  • 参考PWM模式的底层
  • 加入NVIC配置,以及捕获比较中断
  • TIM_Period为:0xFFFF
  • TIM_Pulse为:CH2_Val
  • TIM_OCMode为:TIM_OCMode_Toggle也可以是PWM1
  • 初始化计数器值和比较值
	TIM_SetCounter(TIM2, 0);//计数器清0
	TIM_SetCompare2(TIM2, 0);//设置比较值
  • 开启捕获比较中断TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE),注意对应的定时器以及通道
  • 中断里面
    • 先获取当前的捕获值:capture = TIM_GetCapture2(TIM2);
    • 然后通过设置一个静态表示为分别设置相应的高电平和低电平比较值,别忘了变化标志位的值
    • 注意中断里面获取当前的比较值(捕获值)用TIM_GetCapture2,当然也可以用TIM_GetCounter但是计数器是不断在走的,可能会有些许的不准确
    • 别忘了初始化的时候清零计数器的值以及比较值
		if(flag)
		{
			TIM_SetCompare2(TIM2, capture+CH2_Duty);
		}
		else
		{
			TIM_SetCompare2(TIM2, capture+(CH2_Val-CH2_Duty));
		}
		flag ^= 1;
  • 别忘了开启对应的时钟
  • 别因为复制,写错了定时器名字,是定时器2还是3
  • 还要注意是通道几输出…
  • 计算占空比对应的计数器值时,别忘了是TIM2_CH2_VAL * duty / 100中的除以100!!!

13、输入捕获

  • 参考例程TIM\InputCapture
  • 基本上仍类似于输出比较产生PWM的底层,但是要注意的是,引脚模式需要设置为浮空输入
  • TIM_Period仍然为0xFFFF;
  • 开启捕获比较中断TIM_ITConfig(TIM3, TIM_IT_CC2, ENABLE);
  • TIM3_CH2_CAPTURE_MODE表示当前捕获所在位置
  • mode=0的时候表示刚开始,此时需要更新mode;初始化H和HL,设置计数器;改变触发方式为下降沿触发(初始化为上升沿触发)函数为TIM_OC2PolarityConfig(TIM3, TIM_ICPolarity_Falling);
  • mode=1的时候:更新mode;获取H的值TIM_GetCounter(TIM3);;改变触发方式为上升沿触发。
  • mode=2的时候,更新mode的值,获取HL的值
  • 最后在主函数中处理mode=3的情况,而后算出对应的占空比(第一次计数值/第二次计数值)(要想正确显示需要扩大100倍)和频率(1000000/第二次计数值),显示出来。最后别忘了吧mode模式重新置为0
  • 傻了,,,,当然还需要配置定时器啊,不然怎么计数!!!
  • 第一步想,肯定要从头开始,也就意味着清除第一次、第二次捕获值和计数器的初值,然后改变捕获方向
  • 第二步肯定还需要,改变捕获方向,下降沿捕获
  • 竟然忘了写NCIV向量
  • IO模式是浮空输入,开启输入捕获2是TIM_ITConfig

14、高级定时器的互补PWM输出

  • 分别对应CH×和CH×_N
  • 基本和普通的PWM模式配置一样,只是需要加上互补引脚的一些配置
    • 互补引脚的GPIO(当然包括对应的时候啦)、TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;、以及TIM_CtrlPWMOutputs(TIM1, ENABLE);这个三个配置是必须要的,不然根本不会输出PWM
    • 其他附加的配置,可以TIM_OCNPolarityTIM_OCPolarity一样、TIM_OCIdleState为Set、TIM_OCNIdleState为Reset,但是似乎不配置这三个也可以的!

你可能感兴趣的:(#,蓝桥杯之嵌入式组)