蓝桥杯嵌入式入门学习笔记

1.GPIO,LED的点亮

IDR端口输入寄存器,读取输入状态GPIO的值。(只读并只能以字(16位)的形式读出)

ODR端口输出寄存器,设置输出状态的GPIO的值。(可读可写并只能以字(16位)的形式操作) 

BSRR端口位设置/清除寄存器,设置单个位的输出值。设置单个位的值,可以置1和0。(只能写入并只能以字(16位)的形式操作)

BRR端口位清除寄存器,清除单个位的输出值。只能用于置0。(只能写入并只能以字(16位)的形式操作)

注:BSRR和BRR本质是操作ODR寄存器。

贴上代码如下:LED_Display传入的值1为亮0为灭

//led.h
#define LED1	(1)
#define LED2	(1<<1)
#define LED3	(1<<2)
#define LED4	(1<<3)
#define LED5	(1<<4)
#define LED6	(1<<5)
#define LED7	(1<<6)
#define LED8	(1<<7)

extern u8 LED_data;

//led.c
u8 LED_data=0;

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD, ENABLE);

	{
		GPIO_InitTypeDef GPIO_InitStructure;
		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);
		
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//PC 8-15
		GPIO_Init(GPIOC,&GPIO_InitStructure);
	}
	
	LED_Display(0x00);
}

/*******************************************************************************
* Function Name  : LED_Display
* Description    : 
* Input          : 8位值,1为亮0为灭
* Output         : None
* Return         : None
*******************************************************************************/
//LED_Display( LED_data ^= LED1 );   	单个LED翻转
//LED_Display( LED_data |= LED1 );		点亮单个LED
//LED_Display( LED_data &= ~LED1 );		关闭单个LED	
void LED_Display(u8 data)
{
	GPIOC->ODR = (~data)<<8;
//	GPIOD->BRR = GPIO_Pin_2;//573置0
	GPIOD->BSRR = GPIO_Pin_2;//573置1
	GPIOD->BRR = GPIO_Pin_2;//573置0  防止写lCD干扰到LED的状态
}

2.KEY,按键的使用

前言:在学习按键的时候看到网络上有两大类按键的配置

一类是扫描、占用CPU时间、反应不是非常及时、

一类是中断、不好消抖、进中断后打断主程序、

经考虑后还是决定使用扫描模式。

贴上代码如下:主要思路是在Systick_Hander里置位标志位,在main函数主循环里调用按键扫描函数。


void SysTick_Handler(void)
{
	static u8 num_20ms=1;

	if(++num_20ms >= 20)
	{
	 	num_20ms = 0;
		KeyScan_flag = SET;
	}
}

int main(void)
{
	SysTick_Config(SystemCoreClock/1000);//1ms
	Key_Init();

	while(1)
	{
		if( KeyScan_flag != RESET )
		{
			KeyScan_flag = RESET;
			Key_Scan();
		}

    }
}

void Key_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);

	{
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_8;	//PA0 PA8
		GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
		GPIO_Init(GPIOA,&GPIO_InitStructure);
		
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;	//PB1 PB2
		GPIO_Init(GPIOB,&GPIO_InitStructure);
	}
	
}

void Key_Scan(void)//20ms 扫描一次     支持长按
{
	static u8 key_temp=KEY_OFF,key_clear=0,key_weit=0;
	
	if( (KEY_READ_B1==RESET)||(KEY_READ_B2==RESET)||(KEY_READ_B3==RESET)||(KEY_READ_B4==RESET) ) 
	{
		++key_clear;
//		if( key_clear == 2 )
//		{
//			if(KEY_READ_B1==0x00) {key_temp = KEY_B1;}
//			else if(KEY_READ_B2==0x00) {key_temp = KEY_B2;}
//			else if(KEY_READ_B3==0x00) {key_temp = KEY_B3;}
//			else if(KEY_READ_B4==0x00) {key_temp = KEY_B4;}
//			key_weit = 1; 
//		}
//		else if( key_clear >= 40)//20*40=800ms	长按
//		{
//			key_long_data = key_temp;
//			key_clear = 2+1;
//		}
		if( key_clear >= 40)//20*40=800ms	长按
		{
			key_long_data = key_temp;
			key_clear = 2+1;//不进入短按读取按键程序节约CPU时间
		}
		else if( key_clear == 2 )//短按
		{
			if(KEY_READ_B1==RESET) {key_temp = KEY_B1;}
			else if(KEY_READ_B2==RESET) {key_temp = KEY_B2;}
			else if(KEY_READ_B3==RESET) {key_temp = KEY_B3;}
			else if(KEY_READ_B4==RESET) {key_temp = KEY_B4;}
			key_weit = 1; 
		}
	}
	else 
	{
		key_clear = 0;

		if( key_weit == 1 )//按下后松手
		{
		 	key_weit = 0;

			if( key_long_data == KEY_OFF ) { key_data = key_temp; }//短按
			else { key_long_data = KEY_OFF; }
			
			key_temp = KEY_OFF;
		}
	}	
}

3.USART的配置

printf重定向

其实printf重定向就是我们将printf重新定向到串口,也就是我们可以自己重写C的库函数,当连接器检查到用户编写了与C库函数相同的名字,优先采用 用户编写的函数这样用户就可以实现对库的修改了。printf函数实际是一个宏,最终调用的是 fputc(int ch,FILE *f)这个函数,所以我们需要修改这个函数。

下面我们着重分析一下fputc函数:

    这个库函数调用了两个ST库函数,分别是:USART_GetFlagStatus()与USART_SendData(),形参ch表示串口将要发送的数据,也就是说。当使用printf()时,它先调用fpuc()函数,然后使用ST库的串口发送函数USART_SendData(),把数据转移到发送数据寄存器TDR.触发我们的串口向PC发送一个相应的数据,调用完USART_SendData()之后,使用 while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET)语句不停的检查串口的数据是否发送完成的标志位TC,一直检查到标志为“完成”,才进行下一步操作,

注意:由于fputc()函数的形参调用了C库的FILE,所以在程序中加入stdio.h这个头文件,便且在keil的编译器的设置中勾选Use MicroLIB(使用微库)正点原子的重定向与这个类似不过他没有用微库(Use MicroLIB)。

int fputc(int ch, FILE *f)						//串口重定向标准输出函数
{
	USART_SendData(USART2, (uint8_t)ch);
	while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
	return ch;
}

贴上代码

//usart.h
#define USART2_RX_BUF_SIZE	64

typedef struct 
{
	u8 RX_BUF[USART2_RX_BUF_SIZE];//数据缓存
	vu8 SIZE;//接收到字节数
	vu8 FLAG;//接收完一帧标志	
}Usart2_Data_Structure;

extern Usart2_Data_Structure USART2_DATA;


//usart.c
Usart2_Data_Structure USART2_DATA={0};

void USART2_Init(void)
{	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	{
		GPIO_InitTypeDef GPIO_InitStructure; //PA2
		...
		GPIO_Init(GPIOA,&GPIO_InitStructure);
	}
	{
	 	USART_InitTypeDef USART_InitStructure;
		...
		USART_Init(USART2, &USART_InitStructure);
	}
	{
	 	NVIC_InitTypeDef NVIC_InitStructure;
		...
		NVIC_Init(&NVIC_InitStructure);
	}

	USART_Cmd(USART2, ENABLE);
	USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//接受中断
	USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//空闲中断
}


void USART2_IRQHandler(void)
{
	static u8 size=0;
	
	if( USART_GetITStatus(USART2, USART_IT_RXNE) != RESET )
	{
	  	USART_ClearITPendingBit(USART2, USART_IT_RXNE);	
		
		USART2_DATA.RX_BUF[size] = USART_ReceiveData(USART2);		

		++size;

	}
	if( USART_GetITStatus(USART2, USART_IT_IDLE) != RESET )
	{/*USART_IT_IDLE 空闲中断要用   USART_ReceiveData(USARTx);  
或者  USARTx->SR; USARTx->DR;   清除中断。USART_ClearITPendingBit清除不了。*/
	  	USART_ReceiveData(USART2);//清除中断	
		USART2_DATA.RX_BUF[size] = '\0';
		USART2_DATA.SIZE = size;
		size = 0;
		USART2_DATA.FLAG = 1;

	} 
}

4.ADC的配置

ADC初始化

{

使能ADC时钟

ADC时钟6分频(ADC时钟超过14MHz,可能不准)

配置GPIO

配置ADC

使能ADC

ADC校准

}

ADC读值

{

ADC规则组通道配置

设置软件触发

等待转换完成

读值

}

extern void Delay_Ms(u32 nTime);

void ADC1_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_ADC1, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);

	{
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//
		GPIO_Init(GPIOB,&GPIO_InitStructure);
	}
	{
	 	ADC_InitTypeDef ADC_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);
	}

	ADC_Cmd(ADC1, ENABLE);


	ADC_ResetCalibration(ADC1);
	while( ADC_GetResetCalibrationStatus(ADC1) );
	ADC_StartCalibration(ADC1);
	while( ADC_GetCalibrationStatus(ADC1) );
}

u16 ADC_GetVal(uint8_t ADC_Channel)
{
	ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_239Cycles5);
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);

	while( ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET );

	return ADC_GetConversionValue(ADC1);
}

u16 ADC_GetAverage(uint8_t ADC_Channel, uint8_t times)
{
	u8 i;
	u32 temp_val=0;

	for(i=0; i<times; i++)
	{
		temp_val += ADC_GetVal(ADC_Channel);
	
		Delay_Ms(5);
	}

	return (temp_val/times);
}

5.TIM时钟的应用

5.1.PWM

时钟使能

GPIO配置

TIM_Base配置

TIM_Channel配置

使能CCR预装载寄存器

使能TIM

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);// TIM3 CH1 PA6 TIM3 CH2 PA7         TIM2	CH2 PA1
	{
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7|GPIO_Pin_6;
		GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//
		GPIO_Init(GPIOA,&GPIO_InitStructure);
	}
	{
		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
		TIM_TimeBaseInitStructrue.TIM_Prescaler = 72-1;// 1MHz
		TIM_TimeBaseInitStructrue.TIM_CounterMode = TIM_CounterMode_Up;
		TIM_TimeBaseInitStructrue.TIM_Period = 100-1;		// 1MHz/100=10KHz
		TIM_TimeBaseInitStructrue.TIM_ClockDivision = TIM_CKD_DIV1;
//		TIM_TimeBaseInitStructrue.TIM_RepetitionCounter = ;
		TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructrue);
	}
	{
		TIM_OCInitTypeDef TIM_OCInitStructrue;
		TIM_OCInitStructrue.TIM_OCMode = TIM_OCMode_PWM1;
		TIM_OCInitStructrue.TIM_OutputState = TIM_OutputState_Enable;
		TIM_OCInitStructrue.TIM_Pulse = 20;
		TIM_OCInitStructrue.TIM_OCPolarity = TIM_OCPolarity_High;
		TIM_OC1Init(TIM3, &TIM_OCInitStructrue);//CH1	PA6

		TIM_OCInitStructrue.TIM_Pulse = 50;
		TIM_OC2Init(TIM3, &TIM_OCInitStructrue);//CH2	PA7
	}

	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	
//	TIM_SetCompare2(TIM3,20);

5.2.比较捕获模式(特殊模式PWM捕获模式)

详细配置要求参考STM32中文参考手册V10   14.2.6节PWM输入模式

void TIM2_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// TIM2 CH2 PA1
	{
		GPIO_InitTypeDef GPIO_InitStructure;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
//		GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//
		GPIO_Init(GPIOA,&GPIO_InitStructure);
	}
	{
		TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
		TIM_TimeBaseInitStructrue.TIM_Prescaler = 72-1;// 1MHz
		TIM_TimeBaseInitStructrue.TIM_CounterMode = TIM_CounterMode_Up;
		TIM_TimeBaseInitStructrue.TIM_Period = 0xffff;
		TIM_TimeBaseInitStructrue.TIM_ClockDivision = TIM_CKD_DIV1;
		TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructrue);
	}
	{
		TIM_ICInitTypeDef TIM_ICInitStructrue;
		TIM_ICInitStructrue.TIM_Channel =  TIM_Channel_2;
		TIM_ICInitStructrue.TIM_ICPolarity =  TIM_ICPolarity_Rising;
		TIM_ICInitStructrue.TIM_ICSelection =  TIM_ICSelection_DirectTI;
		TIM_ICInitStructrue.TIM_ICPrescaler =  TIM_ICPSC_DIV1;
		TIM_ICInitStructrue.TIM_ICFilter =  0x00;
		TIM_PWMIConfig(TIM2, &TIM_ICInitStructrue);//CH2	PA1
	}
	TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);//选择有效的触发输入信号:置TIMx_SMCR寄存器中的TS=101(选择TI1FP1)。

	TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);//配置从模式控制器为复位模式:置TIMx_SMCR中的SMS=100。

	TIM_Cmd(TIM2, ENABLE);  //使能TIM3
}

频率与占空比的计算公式

            Frequency = 1000000.0/(IC2Value+1);
            DutyCycle = (IC1Value+1)*1.0/(IC2Value+1);

单片机组,和嵌入式组的相关代码资源可以在我的主页下载。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值