• 利用单片机及4位LED数码管做成四位脉宽显示 ,在一个脉宽期间对内部周期进行计数,得到的一个高电平脉冲内的计数值显示在四位数码管上,并达到相应的技术指标要求。
  • 频率脉冲宽度测量

    2020-07-07 23:26:02
    基于51单片机测量频率脉冲,其中用按键来控制测量的方法。里面包含仿真设计和C语言程序设计。
  • STM32F1系列的定时器中有DMA Burst Feature,配合参考手册上所讲的PWM输入模式,可以全自动地测量一组脉冲宽度,期间CPU可做其他的事情。 DHT11传感器是单总线器件,主机端发出一个开始信号后,该器件会反馈给主机...

    STM32F1系列的定时器中有DMA Burst Feature,配合参考手册上所讲的PWM输入模式,可以全自动地测量一组脉冲的宽度,期间CPU可做其他的事情。

    DHT11传感器是单总线器件,主机端发出一个开始信号后,该器件会反馈给主机42个由高电平+低电平组成的脉冲。主机通过分析这些脉冲的时间宽度解码出器件发来的数据。

    类似的器件还有红外遥控接收头,脉冲的个数也是固定的,只不过不需要发送起始信号,数据是随时都可能收到。


    本例以DHT11传感器为例,DHT11的VCC接3.3V,数据线外接10kΩ的上拉电阻后接到单片机的PA1口上,对应的通道是定时器2的通道2。

    板子完全是笔者自己焊的,接了一个8MHz的HSE晶振,谐振电容为20pF。程序的下载方式为USART1串口下载(通过右下角的开关切换BOOT0=1,PB2接10kΩ下拉电阻到GND),使用的下载软件是STMFlashLoader Demonstrator。BOOT0接10kΩ下拉电阻到GND,再接一个开关直接到VCC。开关闭合后按复位键可进入程序下载模式,开关断开时按复位键运行程序。

    左下角的按键为复位按键,复位引脚NRST无需接上拉电阻,直接接一个0.1μF的电容到GND就行了,复位按键并联在电容器两端。最上面那个黑色的3脚器件是5V转3.3V的AMS1117电压转换器。

    板子高清图:

    程序下载软件:


    【寄存器版程序】

    #include <stdio.h>
    #include <stm32f10x.h>
    
    uint16_t data[84]; // DHT11需要传输84个数据, 即测量42个脉冲
    
    #define DHT11_W0 (GPIOA->BRR = GPIO_BRR_BR1)
    #define DHT11_W1 (GPIOA->BSRR = GPIO_BSRR_BS1)
    //#define DHT11_R ((GPIOA->IDR & GPIO_IDR_IDR1) != 0) // 配置为开漏输出时可直接读取IDR寄存器, 无需切换为输入模式
    
    // 延时n毫秒(0<n<6553)
    void delay(uint16_t nms)
    {
    	TIM2->ARR = 10 * nms - 1;
    	TIM2->PSC = 7199; // 72MHz/7200=10kHz -> 100us
    	TIM2->CR1 = TIM_CR1_OPM | TIM_CR1_URS; // OPM=1: 自动关闭定时器, URS=1: UG=1时保持UIF=0
    	TIM2->EGR = TIM_EGR_UG;
    	TIM2->CR1 |= TIM_CR1_CEN;
    	while ((TIM2->SR & TIM_SR_UIF) == 0);
    	TIM2->SR &= ~TIM_SR_UIF;
    }
    
    // 工程属性里的Use MicroLIB必须打勾
    int fputc(int ch, FILE *fp)
    {
    	if (fp == stdout)
    	{
    		if (ch == '\n')
    		{
    			while ((USART1->SR & USART_SR_TXE) == 0);
    			USART1->DR = '\r';
    		}
    		while ((USART1->SR & USART_SR_TXE) == 0);
    		USART1->DR = ch;
    	}
    	return ch;
    }
    
    void display(void)
    {
    	uint8_t i;
    	uint8_t n = sizeof(data) / sizeof(uint16_t) - DMA1_Channel7->CNDTR; // 总个数减去DMA未传输的个数 = 成功测量的电平个数
    	for (i = 0; i < n; i++)
    	{
    		printf("[ID%02d] ", i);
    		if (i % 2 == 0)
    			printf("High: %dus\n", data[i]); // 高电平的宽度 = CCR1
    		else
    			printf("Low: %dus\n", data[i] - data[i - 1]); // 低电平的宽度 = CCR2 - CCR1
    	}
    }
    
    void measure(void)
    {
    	// 起始信号: 先拉低总线18ms, 然后释放总线
    	DHT11_W0;
    	delay(18);
    	DHT11_W1;
    	// 释放总线后可通过IDR寄存器直接读取总线上的电平(判断是否被器件拉低), 无需改变CRL中的配置
    	
    	DMA1_Channel7->CMAR = (uint32_t)data;
    	DMA1_Channel7->CPAR = (uint32_t)&TIM2->DMAR;
    	DMA1_Channel7->CNDTR = sizeof(data) / sizeof(uint16_t);
    	DMA1_Channel7->CCR = DMA_CCR7_MSIZE_0 | DMA_CCR7_PSIZE_0 | DMA_CCR7_MINC | DMA_CCR7_EN; // 16位传输模式
    	
    	TIM2->ARR = 199; // 超时时间(高电平+低电平)定义为200us
    	TIM2->PSC = 71; // 72MHz/72=1MHz -> 1us
    	TIM2->CR1 = TIM_CR1_URS; // OPM必须为0, 否则只能测量一个脉冲
    	TIM2->EGR = TIM_EGR_UG;
    	
    	// 因为总线最终会回到高电平, 所以最后一次采集肯定是通道2
    	TIM2->CCMR1 = TIM_CCMR1_CC1S_1 | TIM_CCMR1_CC2S_0; // 通道1~2都连接到TIM2_CH2(PA1)引脚上
    	TIM2->SMCR = TIM_SMCR_TS_2 | TIM_SMCR_TS_1 | TIM_SMCR_SMS_2; // 通道2上的事件使定时器清零
    	TIM2->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC2E; // 通道1负责下降沿, 通道2负责上升沿
    	
    	TIM2->DCR = TIM_DCR_DBL_0 | (((uint8_t *)&TIM2->CCR1 - (uint8_t *)TIM2) >> 2); // 每次传输CCR1和CCR2两个寄存器的内容
    	TIM2->DIER = TIM_DIER_CC2DE; // 打开输入捕获通道2的DMA请求
    	
    	TIM2->CR1 |= TIM_CR1_CEN; // 打开定时器, 开始测量
    	while ((TIM2->SR & TIM_SR_UIF) == 0 && (DMA1->ISR & DMA_ISR_TCIF7) == 0); // 这期间CPU可以做其他事情
    	
    	TIM2->CR1 &= ~TIM_CR1_CEN; // 关闭定时器
    	DMA1_Channel7->CCR &= ~DMA_CCR7_EN; // 关闭DMA
    	if (DMA1->ISR & DMA_ISR_TCIF7)
    		DMA1->IFCR = DMA_IFCR_CTCIF7; // 成功
    	else
    	{
    		// 失败: 若脉冲长度超过允许值(TIM2->ARR), 则会导致定时器溢出, 此时给出错误信息
    		TIM2->SR &= ~TIM_SR_UIF;
    		printf("Timeout!!! Remaining: %d bytes\n", DMA1_Channel7->CNDTR);
    	}
    	display(); // 显示实际测量的数据
    	
    	// 完毕后恢复寄存器设置
    	TIM2->SMCR = 0;
    	TIM2->CCER = 0;
    	TIM2->DIER = 0;
    }
    
    int main(void)
    {
    	RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    	RCC->APB1ENR = RCC_APB1ENR_TIM2EN;
    	RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
    	
    	DHT11_W1; // 防止配置为输出模式后总线被拉低
    	GPIOA->CRL = 0x44444464; // PA1接DHT11引脚, 设为开漏输出, 必须外接10K上拉电阻
    	GPIOA->CRH = 0x444444b4; // PA9为USART1发送引脚, 设为复用推挽输出
    	
    	// 串口波特率设为110592
    	USART1->BRR = 625;
    	USART1->CR1 = USART_CR1_UE | USART_CR1_TE;
    	
    	while (1)
    	{
    		printf("-------------------------------------------------\n");
    		measure();
    		delay(5000);
    	}
    }
    【程序运行结果】

    -------------------------------------------------
    [ID00] High: 10us
    [ID01] Low: 83us
    [ID02] High: 86us
    [ID03] Low: 54us
    [ID04] High: 23us
    [ID05] Low: 54us
    [ID06] High: 23us
    [ID07] Low: 54us
    [ID08] High: 70us
    [ID09] Low: 54us
    [ID10] High: 23us
    [ID11] Low: 54us
    [ID12] High: 70us
    [ID13] Low: 54us
    [ID14] High: 70us
    [ID15] Low: 54us
    [ID16] High: 23us
    [ID17] Low: 54us
    [ID18] High: 23us
    [ID19] Low: 54us
    [ID20] High: 23us
    [ID21] Low: 54us
    [ID22] High: 23us
    [ID23] Low: 54us
    [ID24] High: 23us
    [ID25] Low: 54us
    [ID26] High: 23us
    [ID27] Low: 54us
    [ID28] High: 23us
    [ID29] Low: 54us
    [ID30] High: 23us
    [ID31] Low: 54us
    [ID32] High: 23us
    [ID33] Low: 54us
    [ID34] High: 25us
    [ID35] Low: 54us
    [ID36] High: 23us
    [ID37] Low: 54us
    [ID38] High: 23us
    [ID39] Low: 54us
    [ID40] High: 23us
    [ID41] Low: 54us
    [ID42] High: 70us
    [ID43] Low: 54us
    [ID44] High: 70us
    [ID45] Low: 54us
    [ID46] High: 23us
    [ID47] Low: 54us
    [ID48] High: 23us
    [ID49] Low: 54us
    [ID50] High: 23us
    [ID51] Low: 54us
    [ID52] High: 23us
    [ID53] Low: 54us
    [ID54] High: 23us
    [ID55] Low: 54us
    [ID56] High: 23us
    [ID57] Low: 54us
    [ID58] High: 23us
    [ID59] Low: 54us
    [ID60] High: 23us
    [ID61] Low: 54us
    [ID62] High: 23us
    [ID63] Low: 54us
    [ID64] High: 23us
    [ID65] Low: 54us
    [ID66] High: 25us
    [ID67] Low: 54us
    [ID68] High: 23us
    [ID69] Low: 54us
    [ID70] High: 70us
    [ID71] Low: 54us
    [ID72] High: 23us
    [ID73] Low: 54us
    [ID74] High: 23us
    [ID75] Low: 54us
    [ID76] High: 23us
    [ID77] Low: 54us
    [ID78] High: 70us
    [ID79] Low: 54us
    [ID80] High: 23us
    [ID81] Low: 54us
    [ID82] High: 21us
    [ID83] Low: 56us

    由程序运行结果可知,主机发出起始信号后,隔了10μs后,DHT将总线拉低83μs,再释放86μs作为应答信号。最后连续发送40位数据,并以56μs的低电平结束,释放总线。

    位数据0格式:50μs低电平 + 26~28μs高电平

    位数据1格式:50μs低电平 + 70μs高电平

    display函数中采用向下舍入的方式计算时间。若CNT=0,则认为是0μs;若CNT=10,则认为是10μs(实际持续的时间应该是10~11μs之间)。


    【库函数版程序】

    #include <stdio.h>
    #include <stm32f10x.h>
    
    uint16_t data[84]; // DHT11需要传输84个数据, 即测量42个脉冲
    
    // 延时n毫秒(0<n<6553)
    void delay(uint16_t nms)
    {
    	TIM_TimeBaseInitTypeDef tim;
    	TIM_SelectOnePulseMode(TIM2, TIM_OPMode_Single); // 自动关闭定时器
    	TIM_UpdateRequestConfig(TIM2, TIM_UpdateSource_Regular); // UG=1时保持UIF=0
    	
    	TIM_TimeBaseStructInit(&tim);
    	tim.TIM_Period = 10 * nms - 1;
    	tim.TIM_Prescaler = 7199; // 72MHz/7200=10kHz -> 100us
    	TIM_TimeBaseInit(TIM2, &tim);
    	TIM_Cmd(TIM2, ENABLE);
    	while (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) == RESET);
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    }
    
    // 工程属性里的Use MicroLIB必须打勾
    int fputc(int ch, FILE *fp)
    {
    	if (fp == stdout)
    	{
    		if (ch == '\n')
    		{
    			while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    			USART_SendData(USART1, '\r');
    		}
    		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    		USART_SendData(USART1, ch);
    	}
    	return ch;
    }
    
    void display(void)
    {
    	uint8_t i;
    	uint8_t n = sizeof(data) / sizeof(uint16_t) - DMA_GetCurrDataCounter(DMA1_Channel7); // 总个数减去DMA未传输的个数 = 成功测量的电平个数
    	for (i = 0; i < n; i++)
    	{
    		printf("[ID%02d] ", i);
    		if (i % 2 == 0)
    			printf("High: %dus\n", data[i]); // 高电平的宽度 = CCR1
    		else
    			printf("Low: %dus\n", data[i] - data[i - 1]); // 低电平的宽度 = CCR2 - CCR1
    	}
    }
    
    void measure(void)
    {
    	DMA_InitTypeDef dma;
    	TIM_ICInitTypeDef tim_ic;
    	TIM_TimeBaseInitTypeDef tim;
    	
    	// 起始信号: 先拉低总线18ms, 然后释放总线
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
    	delay(18);
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
    	// 释放总线后可通过IDR寄存器直接读取总线上的电平(判断是否被器件拉低), 无需改变CRL中的配置
    	
    	dma.DMA_BufferSize = sizeof(data) / sizeof(uint16_t); // 要测量的脉冲个数
    	dma.DMA_DIR = DMA_DIR_PeripheralSRC;
    	dma.DMA_M2M = DMA_M2M_Disable;
    	dma.DMA_MemoryBaseAddr = (uint32_t)data;
    	dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // CCR寄存器为16位寄存器
    	dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    	dma.DMA_Mode = DMA_Mode_Normal;
    	dma.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->DMAR; // 使用定时器DMA Burst Feature
    	dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    	dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    	dma.DMA_Priority = DMA_Priority_Low;
    	DMA_Init(DMA1_Channel7, &dma);
    	DMA_Cmd(DMA1_Channel7, ENABLE);
    	
    	TIM_SelectOnePulseMode(TIM2, TIM_OPMode_Repetitive); // 不自动关闭定时器, 否则只能测出第一个脉冲的宽度
    	TIM_TimeBaseStructInit(&tim);
    	tim.TIM_Period = 199; // 定义超时时间: 每组高电平+低电平持续的时间总和不得超过200us, 否则认为采集失败
    	tim.TIM_Prescaler = 71; // 72MHz/72=1MHz -> 1us, 以微秒为单位
    	TIM_TimeBaseInit(TIM2, &tim);
    	
    	// 因为总线最终会回到高电平, 所以最后一次采集肯定是通道2
    	tim_ic.TIM_Channel = TIM_Channel_2;
    	tim_ic.TIM_ICFilter = 0;
    	tim_ic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 通道1负责下降沿, 通道2负责上升沿
    	tim_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    	tim_ic.TIM_ICSelection = TIM_ICSelection_DirectTI; // 通道1~2都连接到TIM2_CH2(PA1)引脚上
    	TIM_PWMIConfig(TIM2, &tim_ic);
    	
    	// 通道2上的事件使定时器清零
    	TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);
    	TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
    	
    	// 每次触发通道2输出比较时产生两个16位的DMA请求, 分别传输CCR1和CCR2寄存器的内容
    	TIM_DMAConfig(TIM2, TIM_DMABase_CCR1, TIM_DMABurstLength_2Transfers);
    	TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);
    	
    	// 开始测量
    	TIM_Cmd(TIM2, ENABLE); // 打开定时器
    	while (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) == RESET && DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET); // 这期间CPU可以做其他事情
    	TIM_Cmd(TIM2, DISABLE); // 关闭定时器
    	DMA_Cmd(DMA1_Channel7, DISABLE); // 关闭DMA
    	
    	if (DMA_GetFlagStatus(DMA1_FLAG_TC7) == SET)
    		DMA_ClearFlag(DMA1_FLAG_TC7); // 成功
    	else
    	{
    		// 失败: 若脉冲长度超过允许值(TIM2->ARR), 则会导致定时器溢出, 此时给出错误信息
    		TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    		printf("Timeout!!! Remaining: %d bytes\n", DMA_GetCurrDataCounter(DMA1_Channel7));
    	}
    	display(); // 显示实际测量的数据
    	
    	// 完毕后恢复定时器设置
    	TIM_InternalClockConfig(TIM2); // 关闭定时器的Slave模式
    	TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Disable); // 关闭输入捕获模式
    	TIM_CCxCmd(TIM2, TIM_Channel_2, TIM_CCx_Disable);
    	TIM_DMACmd(TIM2, TIM_DMA_CC2, DISABLE); // 关闭DMA请求
    }
    
    int main(void)
    {
    	GPIO_InitTypeDef gpio;
    	USART_InitTypeDef usart;
    	
    	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
    	
    	// PA1接DHT11引脚, 设为开漏输出, 必须外接10K上拉电阻
    	// 可直接读取端口电平, 无需切换为输入模式
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET); // 防止配置为输出模式后总线被拉低
    	gpio.GPIO_Mode = GPIO_Mode_Out_OD;
    	gpio.GPIO_Pin = GPIO_Pin_1;
    	gpio.GPIO_Speed = GPIO_Speed_2MHz;
    	GPIO_Init(GPIOA, &gpio);
    	
    	// PA9为USART1发送引脚, 设为复用推挽输出
    	gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    	gpio.GPIO_Pin = GPIO_Pin_9;
    	gpio.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &gpio);
    	
    	USART_StructInit(&usart);
    	usart.USART_BaudRate = 115200;
    	usart.USART_Mode = USART_Mode_Tx;
    	USART_Init(USART1, &usart);
    	USART_Cmd(USART1, ENABLE);
    	
    	while (1)
    	{
    		printf("-------------------------------------------------\n");
    		measure();
    		delay(5000);
    	}
    }
    【程序运行结果】
    -------------------------------------------------
    [ID00] High: 1us
    [ID01] Low: 82us
    [ID02] High: 86us
    [ID03] Low: 54us
    [ID04] High: 23us
    [ID05] Low: 54us
    [ID06] High: 23us
    [ID07] Low: 54us
    [ID08] High: 70us
    [ID09] Low: 54us
    [ID10] High: 70us
    [ID11] Low: 54us
    [ID12] High: 23us
    [ID13] Low: 54us
    [ID14] High: 23us
    [ID15] Low: 54us
    [ID16] High: 23us
    [ID17] Low: 54us
    [ID18] High: 23us
    [ID19] Low: 54us
    [ID20] High: 23us
    [ID21] Low: 54us
    [ID22] High: 23us
    [ID23] Low: 54us
    [ID24] High: 23us
    [ID25] Low: 54us
    [ID26] High: 23us
    [ID27] Low: 54us
    [ID28] High: 23us
    [ID29] Low: 54us
    [ID30] High: 23us
    [ID31] Low: 54us
    [ID32] High: 23us
    [ID33] Low: 54us
    [ID34] High: 25us
    [ID35] Low: 54us
    [ID36] High: 23us
    [ID37] Low: 54us
    [ID38] High: 23us
    [ID39] Low: 54us
    [ID40] High: 23us
    [ID41] Low: 54us
    [ID42] High: 70us
    [ID43] Low: 54us
    [ID44] High: 70us
    [ID45] Low: 54us
    [ID46] High: 23us
    [ID47] Low: 54us
    [ID48] High: 23us
    [ID49] Low: 54us
    [ID50] High: 23us
    [ID51] Low: 54us
    [ID52] High: 23us
    [ID53] Low: 54us
    [ID54] High: 23us
    [ID55] Low: 54us
    [ID56] High: 23us
    [ID57] Low: 54us
    [ID58] High: 23us
    [ID59] Low: 54us
    [ID60] High: 23us
    [ID61] Low: 54us
    [ID62] High: 23us
    [ID63] Low: 54us
    [ID64] High: 23us
    [ID65] Low: 54us
    [ID66] High: 25us
    [ID67] Low: 54us
    [ID68] High: 23us
    [ID69] Low: 54us
    [ID70] High: 70us
    [ID71] Low: 54us
    [ID72] High: 23us
    [ID73] Low: 54us
    [ID74] High: 23us
    [ID75] Low: 54us
    [ID76] High: 70us
    [ID77] Low: 54us
    [ID78] High: 23us
    [ID79] Low: 54us
    [ID80] High: 23us
    [ID81] Low: 54us
    [ID82] High: 21us
    [ID83] Low: 56us

    使用库函数后,第一个测量数据出现了很明显的误差。这显然是因为在配置DMA和定时器输入捕获的时候浪费了很多时间。

    在dma.DMA_BufferSize语句前打开定时器4,TIM_DMACmd语句后读取定时器4的CNT值,上述两步直接通过操作寄存器完成。定时器4的分频系数配置为0,测量出来的时间值为CNT=783。因此,库函数总共浪费的时间为784÷72≈10.9μs。


    【根据测量的结果显示湿度和温度数据】

    注意:DHT11测量结果的小数部分始终为0,所以本程序只显示整数结果。

    #include <stdio.h>
    #include <stm32f10x.h>
    
    // 延时n毫秒(0<n<6553)
    void delay(uint16_t nms)
    {
    	TIM_TimeBaseInitTypeDef tim;
    	TIM_SelectOnePulseMode(TIM2, TIM_OPMode_Single); // 自动关闭定时器
    	TIM_UpdateRequestConfig(TIM2, TIM_UpdateSource_Regular); // UG=1时保持UIF=0
    	
    	TIM_TimeBaseStructInit(&tim);
    	tim.TIM_Period = 10 * nms - 1;
    	tim.TIM_Prescaler = 7199; // 72MHz/7200=10kHz -> 100us
    	TIM_TimeBaseInit(TIM2, &tim);
    	TIM_Cmd(TIM2, ENABLE);
    	while (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) == RESET);
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    }
    
    // 工程属性里的Use MicroLIB必须打勾
    int fputc(int ch, FILE *fp)
    {
    	if (fp == stdout)
    	{
    		if (ch == '\n')
    		{
    			while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    			USART_SendData(USART1, '\r');
    		}
    		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    		USART_SendData(USART1, ch);
    	}
    	return ch;
    }
    
    void measure(void)
    {
    	uint8_t data[5] = {0};
    	uint8_t i, j;
    	uint16_t timing[84]; // DHT11需要传输84个数据, 即测量42个脉冲
    	
    	DMA_InitTypeDef dma;
    	TIM_ICInitTypeDef tim_ic;
    	TIM_TimeBaseInitTypeDef tim;
    	
    	// 起始信号: 先拉低总线18ms, 然后释放总线
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
    	delay(18);
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
    	// 释放总线后可通过IDR寄存器直接读取总线上的电平(判断是否被器件拉低), 无需改变CRL中的配置
    	
    	dma.DMA_BufferSize = sizeof(timing) / sizeof(uint16_t); // 要测量的脉冲个数
    	dma.DMA_DIR = DMA_DIR_PeripheralSRC;
    	dma.DMA_M2M = DMA_M2M_Disable;
    	dma.DMA_MemoryBaseAddr = (uint32_t)timing;
    	dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // CCR寄存器为16位寄存器
    	dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
    	dma.DMA_Mode = DMA_Mode_Normal;
    	dma.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->DMAR; // 使用定时器DMA Burst Feature
    	dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    	dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    	dma.DMA_Priority = DMA_Priority_Low;
    	DMA_Init(DMA1_Channel7, &dma);
    	DMA_Cmd(DMA1_Channel7, ENABLE);
    	
    	TIM_SelectOnePulseMode(TIM2, TIM_OPMode_Repetitive); // 不自动关闭定时器, 否则只能测出第一个脉冲的宽度
    	TIM_TimeBaseStructInit(&tim);
    	tim.TIM_Period = 199; // 定义超时时间: 每组高电平+低电平持续的时间总和不得超过200us, 否则认为采集失败
    	tim.TIM_Prescaler = 71; // 72MHz/72=1MHz -> 1us, 以微秒为单位
    	TIM_TimeBaseInit(TIM2, &tim);
    	
    	// 因为总线最终会回到高电平, 所以最后一次采集肯定是通道2
    	tim_ic.TIM_Channel = TIM_Channel_2;
    	tim_ic.TIM_ICFilter = 0;
    	tim_ic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 通道1负责下降沿, 通道2负责上升沿
    	tim_ic.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    	tim_ic.TIM_ICSelection = TIM_ICSelection_DirectTI; // 通道1~2都连接到TIM2_CH2(PA1)引脚上
    	TIM_PWMIConfig(TIM2, &tim_ic);
    	
    	// 通道2上的事件使定时器清零
    	TIM_SelectInputTrigger(TIM2, TIM_TS_TI2FP2);
    	TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
    	
    	// 每次触发通道2输出比较时产生两个16位的DMA请求, 分别传输CCR1和CCR2寄存器的内容
    	TIM_DMAConfig(TIM2, TIM_DMABase_CCR1, TIM_DMABurstLength_2Transfers);
    	TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);
    	
    	// 开始测量
    	TIM_Cmd(TIM2, ENABLE); // 打开定时器
    	while (TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) == RESET && DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET); // 这期间CPU可以做其他事情
    	TIM_Cmd(TIM2, DISABLE); // 关闭定时器
    	DMA_Cmd(DMA1_Channel7, DISABLE); // 关闭DMA
    	
    	if (DMA_GetFlagStatus(DMA1_FLAG_TC7) == SET)
    	{
    		// 成功
    		DMA_ClearFlag(DMA1_FLAG_TC7);
    		if (timing[1] - timing[0] >= 70 && timing[1] - timing[0] <= 90 && timing[2] >= 70 && timing[2] <= 90) // 判断应答信号是否正确
    		{
    			// 分析收到的40位数据
    			for (i = 0; i < 40; i++)
    			{
    				j = 2 * i + 3;
    				if (timing[j] - timing[j - 1] < 40 || timing[j] - timing[j - 1] > 60)
    					break;
    				
    				data[i >> 3] <<= 1;
    				if (timing[j + 1] > 40)
    					data[i >> 3] |= 1;
    			}
    			if (i == 40)
    			{
    				if (((data[0] + data[1] + data[2] + data[3]) & 0xff) == data[4]) // 数据校验
    					printf("H:%d%% T:%d\n", data[0], data[2]); // 显示湿度和温度, 忽略小数部分
    				else
    					printf("Error!\n"); // 数据校验错误
    			}
    		}
    	}
    	else
    		TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 失败: 若脉冲长度超过允许值(TIM2->ARR), 则会导致定时器溢出, 此时给出错误信息
    	
    	// 完毕后恢复定时器设置
    	TIM_InternalClockConfig(TIM2); // 关闭定时器的Slave模式
    	TIM_CCxCmd(TIM2, TIM_Channel_1, TIM_CCx_Disable); // 关闭输入捕获模式
    	TIM_CCxCmd(TIM2, TIM_Channel_2, TIM_CCx_Disable);
    	TIM_DMACmd(TIM2, TIM_DMA_CC2, DISABLE); // 关闭DMA请求
    }
    
    int main(void)
    {
    	GPIO_InitTypeDef gpio;
    	USART_InitTypeDef usart;
    	
    	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
    	
    	// PA1接DHT11引脚, 设为开漏输出, 必须外接10K上拉电阻
    	// 可直接读取端口电平, 无需切换为输入模式
    	GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET); // 防止配置为输出模式后总线被拉低
    	gpio.GPIO_Mode = GPIO_Mode_Out_OD;
    	gpio.GPIO_Pin = GPIO_Pin_1;
    	gpio.GPIO_Speed = GPIO_Speed_2MHz;
    	GPIO_Init(GPIOA, &gpio);
    	
    	// PA9为USART1发送引脚, 设为复用推挽输出
    	gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    	gpio.GPIO_Pin = GPIO_Pin_9;
    	gpio.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &gpio);
    	
    	USART_StructInit(&usart);
    	usart.USART_BaudRate = 115200;
    	usart.USART_Mode = USART_Mode_Tx;
    	USART_Init(USART1, &usart);
    	USART_Cmd(USART1, ENABLE);
    	
    	while (1)
    	{
    		measure();
    		delay(1000);
    	}
    }
    【程序运行结果】
    H:44% T:25
    H:45% T:24
    H:44% T:25
    H:45% T:24
    H:44% T:25
    H:45% T:24
    H:45% T:24
    H:44% T:25
    H:45% T:24
    H:45% T:24
    H:45% T:24
    H:44% T:25
    H:44% T:25
    H为湿度,T为温度。

    展开全文
  • 脉宽测量 51单片机 定时器 计数器

    学51也算有一段时间了 以前学的相当的粗糙 很多原理性的东西 都不知道 今天上课的时候听老师说  这个LOW的单片机可以测量脉宽   去手册上看了一下 加上课本上说的  卧槽 真的可以 哈哈  

    直接上手册上的来说吧  

    就是这个东西  TMOD寄存器中的那个GATE位  很明显 手册中说道  当这位置1的时候 只有在INT0(P3.2)引脚为高电平的时候  并且TR0或者TR1置位的时候 定时/计数器才打开  我们就可以利用这个特点来测量一个脉冲的高电平的宽度  就是这个高电平的持续时间  那么怎么测量呢  说一下思路  

    首先  我们让门控位GATE=1   并且启动定时/计数器 TR0  但是这时候寄存器TH0  TL0里面的数值并不会增加  因为INT0这个引脚上还没高电平  我们把要测量的脉冲接到P3.2这个引脚上   当高电平来的时候   TH0和TL0里面的数值就开始每过一个机器周期增加一   知道P3.2引脚上的高电平消失   当高电平消失的时候 自然 TH0和TL0也就停止计数了  这时候我们把TH0和TL0里面的数值读出来   根据时钟和机器周期的关系就可以的出来高电平的时间了  这里 简单算一下 吧   好算一点  假设晶振频率是12MHZ那么机器周期就是1US   如果读出来 TH0+TL0=50000    那么这个高电平的持续时间就是50mS  能明白不 !!!

    我用仿真做了一下  得出的结果基本正确   这里说一下仿真的问题   就是 仿真里面好像默认的51的晶振频率是12MHZ   即使你搭建外部晶振电路也不能改变改变频率 还要复位貌似也不太好用  哎呀 算了 这个不太影响

    直接上代码 上图  看一下 下面的代码  我仿真里面设置的脉冲频率是1KHZ 占空比是50%    由这个代码得到的高电平的时间 理论值是500us   测得位501us 我觉得基本正确

    # include<reg52.h>
    # define uint unsigned int 
    # define uchar unsigned char
    uchar code table1[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; 
    uint High,Low;
    sbit K=P3^2;
    sbit led0=P2^0;  
    sbit led1=P2^1;  
    sbit led2=P2^2;  
    sbit led3=P2^3;
    void delay(uint k)   //延时函数    这个延时是为了数码管  
    {  
        uint i,j;  
        for(i=k;i>0;i--)  
            for(j=113;j>0;j--) ;  
    }
    void Init_T0()
    {
    	TMOD=0x09;
    	TH0=0;
    	TL0=0;
    }
    void Display(disnum)  
    {  
        led0=1;  
        P0=table1[disnum/1000];  
        delay(3);  
        P0=0xff;  
        led0=0;   
      
        led1=1;  
        P0=table1[disnum%1000/100];  
        delay(3);  
        P0=0xff;  
        led1=0;  
      
        led2=1;  
        P0=table1[disnum%100/10];  
        delay(3);  
        P0=0xff;  
        led2=0;  
      
        led3=1;  
        P0=table1[disnum%10];  
        delay(3);  
        P0=0xff;  
        led3=0;  
      
    }
    void Message_Width()
    {
    	while(K);
    	TR0=1;
    	while(!K);
    	while(K);
    	TR0=0;
    	High=TH0;
    	Low=TL0;
    }
    void main()
    {
    	while(1)
    	{
    	   Init_T0();
    	   Message_Width();	
    	   Display(High*256+TL0);
    	}
    }


    此代码得到下图 


    但是无意间让我发现了一个很无奈的问题  我觉得上面的数码管写的太小学生了 于是想让程序少几行 把数码管的函数变更了一下  但是测得脉宽长度居然不一样了 多了8个us即使两个函数执行时间不一样  可是我实在数码管显示函数之前就把脉宽测量完成了 啊  为啥会多这个8us呢   有哪位大佬看到希望指点一下 啊 下面附上代码和图

    # include<reg52.h>
    # define uint unsigned int 
    # define uchar unsigned char
    uchar code table1[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; 
    uchar code table2[]={0x01,0x02,0x04,0x08};
    uchar table3[4]={0};
    uint High,Low,n;
    sbit K=P3^2;
    void delay(uint k)   //延时函数    这个延时是为了数码管  
    {  
        uint i,j;  
        for(i=k;i>0;i--)  
            for(j=113;j>0;j--) ;  
    }
    void  Data_Process(disnum)
    {
    	table3[0]= disnum/1000;
    	table3[1]= disnum%1000/100;
    	table3[2]= disnum%100/10;
    	table3[3]= disnum%1000;
    }
    void Display()  
    {  
    	uchar i;
    	for(i=0;i<4;i++)
    	{
    		P2=table2[i];
    		P0=table1[table3[i]];
    		delay(3);
    		P0=0xff;
    		P2=0; 
    	} 
    }
    void Init_T0()
    {
    	TMOD=0x09;
    	TH0=0;
    	TL0=0;
    }
    void Message_Width()
    {
    	while(K);
    	TR0=1;
    	while(!K);
    	while(K);
    	TR0=0;
    	High=TH0;
    	Low=TL0;
    }
    void main()
    {
    	while(1)
    	{
    		Init_T0();
    		Message_Width();
    		Data_Process(High*256+TL0);
    		Display();
    	}
    }




    展开全文
  • 但是学到了一种很精确测量脉冲宽度的方法。 具体思想是: 利用定时器的内部资源(当GATE = 1时,计数器的停止和开始受TR和INT的电平共同控制),我们这里用定时器0 ,将外部脉冲接在INT0上,配置定时器0和外部...

    今天被老师教育了一下,教育的内容…(省略一万个字),
    这里写图片描述
    但是学到了一种很精确测量脉冲宽度的方法。
    具体思想是:
    这里写图片描述
    利用定时器的内部资源(当GATE = 1时,计数器的停止和开始受TR和INT的电平共同控制),我们这里用定时器0 ,将外部脉冲接在INT0上,配置定时器0和外部中断0。当脉冲是高电平时,计数器(TH0,TL0)计数,当计数器溢出时,触发定时器中断。当脉冲为下降沿时,触发外部中断,此时停止计数,所记下的时间也就是脉冲的宽度。

    代码如下:

    #include <reg51.h>
    #include <intrins.h>
    
    #define uint unsigned int
    #define uLint unsigned long int		//长整型
    
    uLint pulse_w = 0 ;//计算脉冲的时间,用长整型可以达到10的9次方us,如果用uint,最大只能达到65535us(还不到100ms)
    sbit in = P3^2 ;
    
    void Int0 (void) interrupt 0
    {
        pulse_w += TL0 ;
    	TL0 = 0 ;
    }
    
    void Time0(void) interrupt 1
    {
    	pulse_w += 256 ;//计数寄存器溢出,直接加最大值
    }
    
    int main()
    {
    	//初始化
    	TMOD = 0xA ; //定时器0,模式2,GATE0 = 1
    	TH0 = 0 ;    //填初值
    	TL0 = 0 ;
    	TR0 = 0 ;     
    	ET0 = 1 ;//开定时器0中断
    	
    	IT0 = 1 ;//外部中断0下降沿触发中断
    	EX0 = 1 ;//开外部中断0
    	EA = 1 ;//开总中断
    	
    	while(1)
    	{
    		if(in == 0)//见下面的解释
    			TR0 = 1 ;
    
    	}		
    }
    

    信号函数:

    
    signal void test(double cc) 
    {
    	port3 &= ~(0x1<<2)  ;
    	swatch(1) ;
    	port3 |= (0x1<<2) ;
    	swatch(cc) ;
    	port3 &= ~(0x1<<2) ;
    	swatch(0.1) ;
    
    	_break_ = 1 ;
    	  
    }
    

    输入波形(脉冲高电平1s)
    这里写图片描述

    查看变量的值(0xF4240 = 1000000)
    这里写图片描述

    注释1:由于单片机复位后所有port都为高电平,所以如果不做一些措施的话,单片机一复位,计数器就会计数,造成测量误差。我的做法是:开始设TR0= 0,这样port3.2就无法开启计数器。当外部脉冲低电平时,我才让TR0 = 1,这时port3.2才能开启计数器,达到精准计时的要求

    注释2:单片机的晶振为12M,所以时钟周期为1us

    注释3:计算十六进制的数可以使用win7内部的计算器:使用很简单,自己点一点就会了
    这里写图片描述

    今天感觉自己好傻,有些伤。。。

    最近看到一句话感觉很好,分享一下:
    海底月是天上月,眼前人是心上人。
    向来心是看客心,奈何人是剧中人。

    新手小白,欢迎指教。

    展开全文
  • 利用51系列单片机定时器实现脉冲宽度

    STC12C系列增强型单片机片上扩展了基本51单片机的功能,如提供了PCA/PWM接口,定时器能工作在1T模式下(基本51单片机的时钟是Fosc的12分频,1T模式下1分频)。

    PCA可以用于脉宽测量,但是,protues暂不支持该系列单片机的仿真功能,反复烧写也挺麻烦,所以还是先用基本51单片机实现该功能,在后面的博文里在实现PCA测量脉宽。

    实现思路如下:

    TMOD最高位GATEn置位后,Tn启动计数受INTn(Pin3.3)和TRn的共同影响:TRn为1,当INTn引脚输入为高电平时,Tn才允许计数。利用这个功能可测量INTn上正脉冲的宽度。

    先上图(原谅我的制图能力):

    1):1处 在上升沿之前,初始化TMOD,TRn=1;

    2):2处 INTn引脚为高电平,开始计数测量脉宽;

    3):3处 INTn引脚为低电平,测量结束停止计数TRn=0


    再上仿真图:

    1).信号发生器电平选5v方波。注信号发生器的反相端接地,否则正向端只输出2.5v的方波(剩下的2.5v输出反相方波,可以接到示波器上试试),INTn上永远收不到高电平,达不到预期效果。

    2).T0定时器做计数器使用,收到一个负脉冲产生溢出,启动T1;

    3).T0,T1全工作在方式2自动装载计数值模式。

    然后,上代码:

    工作频率12Mhz

    #include <REG52.H> 
    #include <INTRINS.H>
    
    sbit P1_0 = P3^3;
    
    #define MakeByte(target, Hi,Lo) \
    do{ \
    	target |= (((Hi)<<4)|(Lo)); \	
    }while(0); \
    
    #define SetTH(n,val) \
    do{ \
    	TH##n = val; \
    }while(0); \
    
    #define SetTL(n,val)  \
    do{ \
    	TL##n = val; \
    }while(0); \
    
    #define EnableET(n) \
    do{ \
    	ET##n = 0x01; \
    	IE |= 0x80; \
    }while(0); \
    
    unsigned int click;  
    unsigned int oneMs;
    unsigned char getPlusWidth;
    int main()
    {
    	unsigned int totalus=0,maxPlusWidth=0;
    	P3 = 0xFF;
    
    	getPlusWidth = 0;
    	MakeByte(TMOD,0x0A,0x06);
    	SetTH(0,0xff);
    	SetTL(0,0xff);
    	SetTH(1,0x38);
    	SetTL(1,0x38);
    	EnableET(0);
    	EnableET(1); 
    	TR0 = 0x01;
    	while(1)
    	{
    		while(!getPlusWidth);
    		//等待INT1至低
    		while(INT1==0x01);
    		//等待INT1至高电平
    		while(INT1==0x00);
    		//等待INT1至低电平,脉宽结束
    		while(INT1==0x01);
    		TR1 = 0x00;
    
    		totalus = 1000*(oneMs+(click*0.2))+(TL1-TH1);	
    		oneMs = 0;
    	}
    	return 0;
    }
    
    //T0引脚上接受到负跳变
    void IsrT0() interrupt 1
    {
    	TR1 = 0x00;
    	getPlusWidth = 1;		
    	TR1 = 0x01;
    }
    
    void IsrT1() interrupt 3
    {
    	//每次进入中断0.2ms
    	click++;
    	if(click == 5)
    	{
    		oneMs++;
    		click=0;
    	}
    }

    最后 上仿真结果:

    500Hz的方波,脉宽981us

    1kHz的方波,脉宽587us


    2kHz方波,脉宽234us


    展开全文
  • 硬件:STM32F103C8T6 ... 前面一篇文章讲过如何利用定时器测量信号的频率(见[STM32F10x] 利用定时器测量频率),使用的是定时器的捕获/比较单元(Capture/compare),它也可以测量输入信号的脉冲宽度

    转载自:http://www.cnblogs.com/mr-bike/p/4199751.html

    硬件:STM32F103C8T6

      平台: ARM-MDk V5.11

     

      前面一篇文章讲过如何利用定时器测量信号的频率(见[STM32F10x] 利用定时器测量频率),使用的是定时器的捕获/比较单元(Capture/compare),它也可以测量输入信号的脉冲宽度。

      利用定时器测量脉冲宽度有两种方法。

      方法1:

      在捕获中断函数里改变捕获信号的触发沿(上升沿触发改为下降沿触发,或者下降沿触发改为上升沿触发),通过两次触发得到的计数器的差值,来计算出脉冲宽度。这种

      方法需要定时器的配置和[STM32F10x] 利用定时器测量频率方法是一样的,不同的地方在中断函数里修改触发沿,以TIM2, 捕获通道2为例:

    复制代码
            if(CapState == 0)
            {
                
                /* First time capture */
                Val1         = TIM_GetCapture2(TIM2);
                CapState     = 1;
                
                /* Change the trigger */
                TIM2->CCER         |=    1UL << 5;
            }
    
            else if(CapState == 1)
            {
                /* Second time capture */
                Val2 = TIM_GetCapture2(TIM2);
                
                /* Change the trigger */
                TIM2->CCER         &=    ~(1UL << 5);
    /* Capture computation */ if (Val2 > Val1) { CapVal = Val2 - Val1; } else { CapVal = ((0xFFFF - Val1) + Val2); }

           CapState = 0;
         }
    复制代码

     

      当然,在第一次捕获的时候将计数器清零,然后第二次捕获直接读取捕获值也是一个不错的选择,如下,

      

    复制代码
            if(CapState == 0)
            {
                
                /* First time capture */
               TIM_GetCapture2(TIM2);
               CapState     = 1;
                
                /* Change the trigger */
                TIM2->CCER         |=    1UL << 5;
    
                /* Clear CNT */
                TIM_SetCounter(TIM2, 0);
            }
    
            else if(CapState == 1)
            {
                /* Second time capture */
                CapVal = TIM_GetCapture2(TIM2);
                
                /* Change the trigger */
                TIM2->CCER         &=    ~(1UL << 5);

           CapState = 0;      }
    复制代码

      触发沿的配置在CCER这个寄存器里面,这里直接对寄存器进行操作,具体请查阅STM32F10x的参考说明书。

     

      方法2:

      利用定时器的PWM输入模式(PWM input mode)。所谓的PWM模式,其实就是利通了定时器捕获单元映射功能,定时器捕获单元1(IC1)和捕获单元2(IC2)可以映射

      到同一个捕获通道,一个捕获单元配置成捕获上升沿信号,另一个捕获单元配置成捕获下降沿信号,那么两个捕获值的差就是脉冲宽度的值。还是以TIM2, 捕获通道2

      为例,配置代码如下:

    复制代码
     1 void CaptureConfig(void)
     2 {
     3     TIM_ICInitTypeDef  TIM_ICInitStructure;
     4     
     5     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
     6     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
     7     TIM_ICInitStructure.TIM_ICFilter    = 0x0;
     8     TIM_ICInitStructure.TIM_Channel     = TIM_Channel_1;
     9     TIM_ICInitStructure.TIM_ICPolarity  = TIM_ICPolarity_Rising;
    10     TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
    11     
    12     /* TIM enable counter */
    13     TIM_Cmd(TIM2, ENABLE);
    14 
    15     /* Enable the CC1, CC2 Interrupt Request */
    16     TIM_ITConfig(TIM2, TIM_IT_CC2 | TIM_IT_CC1, ENABLE);
    17 }
    复制代码

      各行代码的意思:

        L2:  指定捕获单元的映射方式,TIM_ICSelection_IndirectTI 说明捕获单元1(IC1)和捕获单元2(IC2)映射到捕获通道2(TI2,这里是指捕获通道2,区别

            定时器TIM2),假如该值为TIM_ICSelection_DirectTI,则捕获单元1(IC1)和捕获单元2(IC2)映射到捕获通道1(TI1)。

        L8, L9:  指定捕获单元触发的信号沿(上升沿还是下降沿)。TIM_Channel_1对应捕获单元1(IC1),TIM_Channel_2对应捕获单元2(IC2)注意这里

              只需要指定一个捕获单元,另一个捕获单元将会在调用初始化函数TIM_PWMICConfig时设定为相反的边沿。

        L16:  允许捕获单元1和捕获单元2的中断。

      这样,当捕获通道2(TI2)检测到上升沿时,捕获单元1(IC1)将会记录下计数器的值,同时捕获单元1的中断标志位(CC1IF)将置位;

      当捕获通道2(TI2)检测到下降沿时,捕获单元2(IC2)将会记录下计数器的值,同时捕获单元2的中断中断标志位(CC2F)将置位。

      中断处理函数示例代码如下:

    复制代码
        if(TIM_GetITStatus(DEMOD_CAPTURE_TIM, TIM_IT_CC1) == SET)
        {
            TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
            TIM_GetCapture1(TIM2);
            TIM_SetCounter(TIM2, 0);
                   
        }    
        else if(TIM_GetITStatus(DEMOD_CAPTURE_TIM, TIM_IT_CC2) == SET)
        {
            TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
            CapVal = TIM_GetCapture2(TIM2);
        }
    复制代码

     

      总结:  

          STM32F10x系列的定时器是一个很灵活的东西,想灵活应用它首先要理解它的原理和运作的过程。定时器的捕获单元和捕获通道是比较容易混淆的两个东西

        简单来说,STM32F10x的每个通用定时器有4个捕获通道(TI1, TI2, TI3, TI4, 对应4个GPIO口),每个捕获通道对应一个捕获单元,即IC1, IC2, IC3, IC4,

        其中捕获单元1和捕获单元2可以映射到同一个捕获通道,TI1或者TI2。


    展开全文
  • 用外部中断0测量输入的负脉冲宽度。 #include<reg51.h> sbit si=P3^0; //定义负脉冲输出端口 sbit mi=P3^2; //定义外部中断0输入口 void main() { TMOD=0x22; //T0和T1设置为定时方式2 EA=1; ET0=1; //...
  • 利用单片机的定时方式0,实现外部脉冲宽度(在5到250ms)的测量。要求画出电路图。在线等 ;问题补充:写出程序,测量结果存储在R0中。;=================================================;做而论道对此题给出了答案:...
  • HAL库STM32脉冲宽度和周期测量 使用芯片:STM32F103RCT6 思路:定时器设置为1MHZ的计数频率,定时计数器增加一就是增加1us ① 首先设置为上升沿捕获,捕获上升沿记录此刻的时间计数值; ② 然后切换为下降沿捕获,...
  • 输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。以下是对脉冲宽度及频率的计算。 1、脉冲宽度 如下图所示,采集该高电平脉冲的宽度,只需要...
  • 测定脉冲频率,附带部分...很多场合需要用到对频率的精确测量,譬如你用霍尔传感器做测转速系统,就需要用到此模块,测量霍尔传感器输出的脉冲的频率,然后稍作运算,就可以实现转速的测量功能。本设计利用51单片机
  • 使能输入捕捉功能, 测量计算输入的脉冲宽度: //ICC-AVR application builder : 2006-7-14 10:13:46 // Target : M128 // Crystal: 14.7456Mhz #include  #include  //define PORT B...
  • 1、 设计内容 ...用单片机实现对一路脉冲计数和显示的功能。硬件包括单片机最小系统、LED显示、控制按钮;软件实现检测到显示 2、 要求 计数范围0~2000; 脉冲输入有光电隔离整形, 有清零按钮
  • GATE=1时,定时器由INT和TR共同控制,用于测量脉宽; 2.TCON定时器/计数器控制寄存器 3.   2.应用示例: 2.1T0/T1控制外部引脚输出方波 1.设计思路:设置定时计数器工作在定时工作方式,在中断服...
  • 捕获脉冲宽度

    2020-06-19 05:45:40
    利用LPC900的CCU模块Capture/Compare U ni t 捕获/ 比较模块可以实现对脉冲波形的高精度捕获,因此该功能多应用于一些测量型场合
  • 51单片机测量脉宽

    2019-10-20 15:52:41
    51单片机测量脉宽,数码管显示@[TOC]单片机 #include<REGX51.H> #include<intrins.h> typedef unsigned int uint16_t; typedef unsigned char uint8_t; uint8_t code seg_cc_table [] = {0x3f,0x06,0x5b...
  • PWM(Pulse Width Modulation),一般指脉冲宽度调节,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中,比如LED亮度调节、电机转速控制等。...
1 2 3 4 5 ... 20
收藏数 680
精华内容 272