GPIO multiplexing function 1 - timer input capture

foreword

insert image description here
The general-purpose input and output function of GPIO and its analog timing function were introduced earlier. It can be found that using analog timing to drive peripherals will cause a little trouble in the code part, and some timing and functions cannot be realized with analog timing. At this time, you need to use peripherals. The peripherals here not only refer to the off-chip peripherals, but also the internal and external devices inside the chip.
Most of these on-chip peripherals need to exchange data with off-chip, such as timer input capture, timer comparison output, SPI controller, and I2C controller.
These on-chip peripherals that require data exchange need to use the front GPIO as an intermediary. Because GPIO is the only communication bridge between the inside and outside of the microcontroller, when using these on-chip peripherals, GPIO needs to be configured as a special mode, also called multiplexing mode.
![Insert picture description here](https://img-blog.csdnimg.cn/e7ff1952b78443c09008e92099784d9b.png#pic_center =600*200)
The number of on-chip peripherals of STM32F103C8T6 can be seen through the chip manual. The author's board uses four timers, one SPI, three USARTs, and one ADC. The specific usage will be introduced later.
insert image description here

Timer input capture

In order to connect with the content of the previous article, this article first introduces the use of timer input capture to analyze the data of the infrared receiver tube, and use it as an input signal for control.

Data analysis of infrared reception

In the previous article, we introduced the NEC format data frame received by HS0038. Take a screenshot, as shown in the figure below:
insert image description here
According to this frame format, you can find the following features:
insert image description here

1. The initial adjustment of a frame of data has a synchronization header, which consists of a 9ms low level and a 4.5ms high level;
2. The first half of the logic "1" and logic "0" signals are both 560us Low level, only the high level time in the second half is different, the high level duration of logic "1" is 1680us, and the high level duration of logic "0" is 560us; according to these characteristics, if you want to analyze the
data , you need to locate each rising edge and obtain the high level duration of each part. According to the difference of the high level time, it can be known that the synchronization head of the signal at this time is still logic "0" or logic "1".
It is necessary to accurately locate the edge and record the duration of the level. Does this process look familiar? When the previous series introduced input capture, we used input capture to obtain the length of time the button was pressed, and The object distance is obtained through the ultrasonic module, which is the detection edge plus timing.
If you don’t know the input capture function here, you can check out the author’s introduction, and I won’t repeat it here.
Embedded Study Notes - Input Capture

capture ideas

According to the characteristics of the frame format, you can briefly outline the programming ideas for obtaining infrared remote control signals:
1. First, you need to initialize the corresponding timing channel and GPIO according to the hardware connection;
2. Configure the corresponding time base. Here, for more accurate counting, the best It is best to set the counting period of the timer to 1us once, which is also convenient for data processing. The specific setting method will be introduced later in the programming;
3. Set the trigger mode of input capture and the parameters of whether to divide the frequency,
4. Configure the capture interrupt to capture,
here because the duration of the high level needs to be obtained:
first of all, it must be raised For edge-triggered capture interrupts, clear the count value first after the rising-edge trigger, start counting from 0, and switch the trigger mode to falling-edge triggering, return the counting value when the falling-edge is triggered, and switch back to rising-edge triggering again to judge and compare the counting value The range and the characteristics of each signal can know the data value at this time.
insert image description here
5. In addition to the capture interrupt, you can also turn on the update interrupt as a sign of the completion of the reception. In order to distinguish, the update interrupt time here is 10ms. In the whole process, only after receiving the 32-bit signal, the count value may overflow and generate an update. Interrupt, the generation of an update interrupt means that data processing can be performed.
insert image description here

programming practice

First, find the corresponding GPIO according to the idea. From the schematic diagram and data sheet, you can know that GPIOA1 is used, and channel 2 of TIM2 is multiplexed.
insert image description here

1. Initialize the clock

When using any on-chip peripheral, the first thing is to turn on its corresponding clock. Here we need to turn on the clock of GPIOA and TIM2. Since the multiplexing function is used, the multiplexing clock needs to be turned on.
According to previous experience, the clock-related first look at the data sheet to find the hook-up bus of the on-chip peripherals.
insert image description here
It can be found that the two on-chip peripherals are mounted on APB2 and APB1 respectively, so the required code is as follows:

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIOA外设能模块时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  //使能GPIOA AFIO复用功能模块时钟

It can be seen that there is another AFIO clock in the above code. The reason for turning it on is that the multiplexing function of GPIO needs to be used, so this clock must be turned on.
insert image description here

2. Initialize GPIO

One thing to note here is that for MCUs such as C8T6 with a small number of GPIOs, there will be a remapping setting. Similar to the mapping table in the figure below to achieve. The default of TIM2CH2 here is that PA1 does not need to be remapped.

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState) This function is specially used to implement remapping settings. I won't use it here for the time being, but I'll talk about it later.
insert image description here

So the question is, how to write the initialization of GPIO?
As an input capture, the mode is naturally input; but in the GPIO input mode of M3, there is no multiplexing input mode, here we do not need pull-up and pull-down, and it is not an analog input, so just select the floating input directly.
insert image description here

 //设置该引脚为浮空输入功能,
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM2_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO

3. Configure the clock reference of the timer

The clock reference of the timer is to set the basic parameters of the timer such as the prescaler frequency, reload value, and counting mode of the timer. For a detailed introduction to these things, you can refer to the author's previous series of embedded study notes - general timer .
The idea of ​​using the library function here is actually similar to the previous GPIO. First find the stm32f10x_tim.h in the library function, and then find the corresponding initialization structure. According to the description and the subsequent parameter macro or parameter enumeration, perform the structure members in turn. assignment.
insert image description here
After the parameter assignment is completed, it is also necessary to call the corresponding structure initialization function to write these parameters into the corresponding registers. The parameters are the timer number and the address of the above structure.
insert image description here

//初始化TIM2
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定义定时器结构体
	TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc-1; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

4. Configure input capture parameters

Here we need to use the input capture function of the timer, so we need to further configure it on the basis again. The process is the same as above, and the corresponding structure variable is found, and then refer to the description and parameter macro rain parameter enumeration to assign and initialize. Here you need to configure physical channels, trigger signal types, input capture and mapping channels.
insert image description here
Similarly, after configuring the relevant parameters, it is also necessary to call the corresponding initialization function to write to the register.
insert image description here


	//输入捕获
	TIM_ICInitTypeDef TIM_ICInitStruct;//定时器输入捕获控制寄存器
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;//通道2
	TIM_ICInitStruct.TIM_ICFilter=0x00;//不使用滤波器
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//不分频
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;// TI2FP2 输入捕获通道选择
	TIM_ICInit(TIM2,&TIM_ICInitStruct);

5. Interrupt configuration

Then there is the configuration of the interrupt. First, you need to call the function of the timer to configure the interrupt signal. Here you need to use two types of update interrupt and capture interrupt.
insert image description here
The interrupt signal is only specified above, and the interrupt has not been configured yet, so you need to find
misc.h, which also has the corresponding parameter configuration structure.
insert image description here
There is also a corresponding initialization function
insert image description here
, so the code is as follows:

//中断配置
	TIM_ITConfig(TIM2,TIM_IT_CC2|TIM_IT_Update,ENABLE);//更新中断和捕获中断

	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	 //使能放到最后,避免写入失败。
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2

6. Interrupt service function

The above are just initialization configuration functions, and the real function realization requires the use of interrupt service functions. First, you need to go to the startup file to get the corresponding interrupt service function name.
The idea of ​​the whole code is,
1. Use the rising edge trigger to clear the counter, and switch the trigger mode to the falling edge trigger, the counting cycle is 1us once;
2. When the falling edge triggers, judge the count value and compare it, according to the three types Features, judging whether it is a synchronous header, logic 1 or logic 0;
3. According to the order of the low order, define a 32-bit data, and receive the data bit by bit;
4. When the counter overflows, that is, the count value reaches 10000, which means the contact time is 10ms High level, at this time it is judged that the reception is complete.

//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{
    
     	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
	{
    
    //10MS产生一次中断,整个传输中最长的高电平时间为同步码 的 4.5ms ;只有释放信号才可能进入更新中断	
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	 
		if(Infrared_Receive.start)//接收到过同步头且产生了更新中断说明接收到完毕了
		{
    
    
			Infrared_Receive.start=0;
			Infrared_Receive.end =1;//接收完成的标志位
			
		}
	}
	if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET)
	{
    
    	  
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);	 
		//进入捕获中断,由于需要捕获比较的是高电平的时间,所以第一次捕获为上升沿,
		//检测是否是上升沿使用IO的输入电平来判断
		if(REMOTE_DATA)					//此时是高电平,说明是上升沿检测,
		{
    
    
			//为了统计高电平的时间,进入后需要将计数器的值清零计数一次是1US
			TIM_SetCounter(TIM2,0);  //表示计数值清零
			//切换为下降沿捕获
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);	
		}
		else       //说明是下降沿的捕获中断,
		{
    
    
			//此时需要判断是否为起始(同步头)信号
			//获取此时计数器的值,也就是高电平的持续时间
			Infrared_Receive.ccr = TIM_GetCapture2(TIM2);//获取当前计数值
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);//切换为上升沿捕获
			
			if(Infrared_Receive.start == 1)     //如果已经接收过同步头
			{
    
    
				if(Infrared_Receive.ccr>300 && Infrared_Receive.ccr<800)//560us高电平说明是数据0
				{
    
    
						Infrared_Receive.data=Infrared_Receive.data>>1;//低位在前接收后的格式为:8位控制码反码+8位控制码+8位地址码反码+8位地址码
				}
				else if(Infrared_Receive.ccr>1300 && Infrared_Receive.ccr<1900)//1680us说明是数据1
				{
    
    
						Infrared_Receive.data=Infrared_Receive.data>>1;
						Infrared_Receive.data |=0x80000000;//接收数据为1时,给对应位补1
				}
			}
			else                           //没有接收到同步头,先判断是否有同步头 
			{
    
    
					if(Infrared_Receive.ccr>4000 && Infrared_Receive.ccr<5000)//检测到同步头信号
					{
    
    
						Infrared_Receive.start=1;
					}
			}
		}
	}    
}

7. Processing data frames

After receiving the 32bit data, according to its frame format, we can extract the data we need

/************************************************
函数功能:红外获取控制码
函数名:Infrared_Get_Data
函数形参:None
函数返回值:无控制-1  有控制码则返回控制码
备注:  
**************************************************/
s8 Infrared_Get_Data(void)
{
    
    
	u8 data;  //控制码
	
	if(Infrared_Receive.end==1)//检测到数据接收完毕
	{
    
    
		Infrared_Receive.end=0;//清除标志位,方便下次接收
		
		data = (Infrared_Receive.data & 0x00ff0000) >> 16;  //获取控制码
		
		return data;	
	}
	return -1;
}

Then you can call this function to execute the corresponding function according to different input values.

achieve effect

insert image description here

Summarize

The introduction of the timer input capture to realize the processing of the infrared input signal is here. If there are any deficiencies in the article, please criticize and correct.

Guess you like

Origin blog.csdn.net/qq_41954556/article/details/131140258