STM32F103RC单片机ADC中的间断模式(Discontinous mode)的使用

对于规则通道组,间断模式下每转换一个通道,EOC就会置位一次。因此不必像SCAN模式那样必须采用DMA来搬运数据。

当DISCEN=1时打开间断模式,DISCNUM指定每次转换的通道个数,范围为1~8。

在下面的程序中,DISCNUM=011,每次转换4个通道。L=1001,总共有10个通道。

【程序1】

#include <stdio.h>
#include <stm32f10x.h>

void delay(void)
{
	uint32_t i;
	for (i = 0; i < 2000000; i++);
}

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 convert(void)
{
	uint8_t i, j, n;
	uint16_t result[4];
	for (i = 0; i < 10; i += n)
	{
		ADC1->CR2 |= ADC_CR2_SWSTART;
		n = (i + 4 > 10) ? 10 - i : 4;
		for (j = 0; j < n; j++)
		{
			while ((ADC1->SR & ADC_SR_EOC) == 0);
			result[j] = ADC1->DR;
		}
		
		printf("[Regular SQ%d~%d]", i + 1, i + n);
		for (j = 0; j < n; j++)
			printf(" %d", result[j]);
		printf("\n");
	}
}

int main(void)
{
	uint8_t i = 3;
	
	// 打开外设时钟
	RCC->CFGR |= RCC_CFGR_ADCPRE_1; // ADC时钟设为12MHz, 最大允许时钟为14MHz
	RCC->APB1ENR = RCC_APB1ENR_PWREN;
	RCC->APB2ENR = RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_USART1EN;
	
	GPIOA->CRH = 0x444444b4; // 串口发送端设为复用推挽50MHz输出
	GPIOA->CRL = 0x00000008; // ADC1通道1~7设为模拟, PA0待机唤醒按键设为带下拉电阻输入
	GPIOB->CRL = 0x44444400; // ADC1通道8~9设为模拟
	
	// 规则通道序列
	ADC1->SQR1 = ADC_SQR1_L_3 | ADC_SQR1_L_0; // 10个通道
	ADC1->SQR2 = ADC_SQR2_SQ10_4 | (ADC_SQR2_SQ9_3 | ADC_SQR2_SQ9_0) | ADC_SQR2_SQ8_3 | (ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_1 | ADC_SQR2_SQ7_0);
	ADC1->SQR3 = (ADC_SQR3_SQ6_2 | ADC_SQR3_SQ6_1) | (ADC_SQR3_SQ5_2 | ADC_SQR3_SQ5_0) | ADC_SQR3_SQ4_2 | (ADC_SQR3_SQ3_1 | ADC_SQR3_SQ3_0) | ADC_SQR3_SQ2_1 | ADC_SQR3_SQ1_0;
	
	ADC1->CR1 = ADC_CR1_DISCEN | ADC_CR1_DISCNUM_1 | ADC_CR1_DISCNUM_0; // 每次转换的规则通道数为4
	ADC1->CR2 = ADC_CR2_TSVREFE | ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL | ADC_CR2_ADON; // 打开ADC1, 规则通道设为外部触发模式
	
	USART1->BRR = 0x271; // 波特率: 115200
	USART1->CR1 = USART_CR1_UE | USART_CR1_TE; // 允许发送
	
	while (i--)
		convert();
	
	ADC1->SR &= ~ADC_SR_STRT;
	printf("ADC1->SR=0x%02x\n", ADC1->SR);
	
	// 进入待机模式
	while ((USART1->SR & USART_SR_TC) == 0); // 等待USART1发送完毕
	while (GPIOA->IDR & GPIO_IDR_IDR0)
		delay(); // WKUP按键消抖
	SCB->SCR = SCB_SCR_SLEEPDEEP;
	PWR->CR = PWR_CR_PDDS | PWR_CR_CWUF;
	PWR->CSR = PWR_CSR_EWUP;
	__WFI();
	
	return 0;
}
【运行结果】
[Regular SQ1~4] 302 724 1014 1356
[Regular SQ5~8] 1774 2493 2870 3177
[Regular SQ9~10] 3687 2100
[Regular SQ1~4] 303 724 1014 1356
[Regular SQ5~8] 1776 2493 2867 3177
[Regular SQ9~10] 3689 2100
[Regular SQ1~4] 302 723 1015 1354
[Regular SQ5~8] 1774 2492 2869 3176
[Regular SQ9~10] 3687 2100
ADC1->SR=0x00
由运行结果可知,如果通道组中剩余的通道数大于或等于4个通道,则转换4个通道,否则剩下几个就转换几个。


对于注入通道组,则是每次触发只转换一个通道,DISCNUM的值无效。当整个通道组转换完毕时EOC和JEOC同时置1。

当JDISCEN=1时打开间断模式。注意SCAN必须置1,否则第一次转换时将会出错!

【程序2】

#include <stdio.h>
#include <stm32f10x.h>

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;
}

int main(void)
{
	uint8_t i = 0;
	
	// 打开外设时钟
	RCC->CFGR |= RCC_CFGR_ADCPRE_1; // ADC时钟设为12MHz, 最大允许时钟为14MHz
	RCC->APB1ENR = RCC_APB1ENR_TIM2EN;
	RCC->APB2ENR = RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_USART1EN;
	
	GPIOA->CRH = 0x444444b4; // 串口发送端设为复用推挽50MHz输出
	GPIOA->CRL = 0x00000008; // ADC1通道1~7设为模拟, PA0待机唤醒按键设为带下拉电阻输入
	GPIOB->CRL = 0x44444400; // ADC1通道8~9设为模拟
	
	USART1->BRR = 0x271; // 波特率: 115200
	USART1->CR1 = USART_CR1_UE | USART_CR1_TE; // 允许发送
	printf("-------------------------------------------------------\n");
	
	TIM2->ARR = 3332;
	TIM2->PSC = 7199;
	TIM2->CR1 = TIM_CR1_URS;
	TIM2->CR2 = TIM_CR2_MMS_1; // TRGO=UIF
	TIM2->DIER = TIM_DIER_UIE;
	TIM2->EGR = TIM_EGR_UG; // JEXTTRIG必须为0, 否则将触发转换
	NVIC_EnableIRQ(TIM2_IRQn);
	
	ADC1->JSQR = ADC_JSQR_JL_1 | ADC_JSQR_JSQ4_2 | (ADC_JSQR_JSQ3_1 | ADC_JSQR_JSQ3_0) | ADC_JSQR_JSQ2_1; // 注入通道序列
	ADC1->CR1 = ADC_CR1_SCAN | ADC_CR1_JDISCEN; // 一定要同时把SCAN模式打开!!!否则当EOC第一次置位时只转换了一个通道
	ADC1->CR2 = ADC_CR2_JEXTTRIG | ADC_CR2_JEXTSEL_1 | ADC_CR2_ADON; // 打开ADC1, 注入通道设为TIM2_TRGO触发
	
	TIM2->CR1 |= TIM_CR1_CEN;
	while (1)
	{
		while ((ADC1->SR & ADC_SR_JEOC) == 0);
		ADC1->SR &= ~(ADC_SR_JSTRT | ADC_SR_EOC | ADC_SR_JEOC);
		printf(" %d %d %d %d (ADC1->SR=0x%02x)\n", ADC1->JDR1, ADC1->JDR2, ADC1->JDR3, ADC1->JDR4, ADC1->SR);
		
		if (i == 4)
			ADC1->JSQR |= ADC_JSQR_JL_0 | ADC_JSQR_JSQ1_0; // 增加一个通道
		if (i != 5)
			i++;
	}
}

void TIM2_IRQHandler(void)
{
	TIM2->SR &= ~TIM_SR_UIF;
	printf("*");
}
【运行结果】
-------------------------------------------------------
*** 725 1015 1358 0 (ADC1->SR=0x00)
*** 723 1014 1357 0 (ADC1->SR=0x00)
*** 725 1015 1357 0 (ADC1->SR=0x00)
*** 724 1015 1357 0 (ADC1->SR=0x00)
*** 724 1015 1357 0 (ADC1->SR=0x00)
**** 303 724 1014 1357 (ADC1->SR=0x00)
**** 303 727 1016 1357 (ADC1->SR=0x00)
**** 303 724 1015 1358 (ADC1->SR=0x00)
**** 303 724 1015 1358 (ADC1->SR=0x00)
**** 303 725 1015 1357 (ADC1->SR=0x00)
**** 303 724 1014 1357 (ADC1->SR=0x00)
**** 304 725 1015 1359 (ADC1->SR=0x00)
**** 303 725 1015 1356 (ADC1->SR=0x00)
**** 303 724 1015 1357 (ADC1->SR=0x00)
**** 304 723 1015 1357 (ADC1->SR=0x00)
**** 305 726 1015 1357 (ADC1->SR=0x00)
**** 303 725 1016 1357 (ADC1->SR=0x00)
**** 303 725 1015 1357 (ADC1->SR=0x00)
**** 304 725 1015 1358 (ADC1->SR=0x00)
**** 303 723 1015 1357 (ADC1->SR=0x00)
**** 303 724 1014 1357 (ADC1->SR=0x00)
**** 303 724 1015 1357 (ADC1->SR=0x00)
**** 304 724 1015 1357 (ADC1->SR=0x00)
**** 302 725 1014 1357 (ADC1->SR=0x00)
**** 303 724 1014 1357 (ADC1->SR=0x00)
**** 302 724 1015 1357 (ADC1->SR=0x00)

如果忘记将SCAN置1,则第一排只有一个星号,也就是只触发了一次就完成了一组单通道转换,但后续的转换没有问题。

ADC1->CR1 = /*ADC_CR1_SCAN | */ADC_CR1_JDISCEN;
-------------------------------------------------------
* 724 0 0 0 (ADC1->SR=0x00)
*** 725 1015 1358 0 (ADC1->SR=0x00)
*** 725 1015 1358 0 (ADC1->SR=0x00)
*** 724 1015 1358 0 (ADC1->SR=0x00)
*** 725 1014 1357 0 (ADC1->SR=0x00)
**** 304 723 1016 1357 (ADC1->SR=0x00)
**** 303 725 1015 1357 (ADC1->SR=0x00)


【程序2(库函数版)】

#include <stdio.h>
#include <stm32f10x.h>

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;
}

int main(void)
{
	ADC_InitTypeDef adc;
	GPIO_InitTypeDef gpio;
	TIM_TimeBaseInitTypeDef tim;
	USART_InitTypeDef usart;
	uint8_t i = 0;
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_USART1, ENABLE);
	
	gpio.GPIO_Mode = GPIO_Mode_AF_PP;
	gpio.GPIO_Pin = GPIO_Pin_9;
	gpio.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &gpio);
	
	gpio.GPIO_Mode = GPIO_Mode_AIN;
	gpio.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_Init(GPIOA, &gpio);
	gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_Init(GPIOB, &gpio);
	
	USART_StructInit(&usart);
	usart.USART_BaudRate = 115200;
	usart.USART_Mode = USART_Mode_Tx;
	USART_Init(USART1, &usart);
	USART_Cmd(USART1, ENABLE);
	printf("-------------------------------------------------------\n");
	
	TIM_UpdateRequestConfig(TIM2, TIM_UpdateSource_Regular); // URS=1, 防止TIM_TimeBaseInit产生中断
	TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	NVIC_EnableIRQ(TIM2_IRQn);
	
	TIM_TimeBaseStructInit(&tim);
	tim.TIM_Period = 3332;
	tim.TIM_Prescaler = 7199;
	TIM_TimeBaseInit(TIM2, &tim);
	
	ADC_StructInit(&adc);
	adc.ADC_ScanConvMode = ENABLE;
	ADC_Init(ADC1, &adc);
	
	ADC_InjectedDiscModeCmd(ADC1, ENABLE);
	ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T2_TRGO);
	ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);
	
	ADC_InjectedSequencerLengthConfig(ADC1, 3); // 必须先设置通道数JL
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_2, 1, ADC_SampleTime_1Cycles5); // Rank从1开始, 对应的是JSQR寄存器中的JSQ2
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_1Cycles5); // JSQ3
	ADC_InjectedChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_1Cycles5); // JSQ4
	ADC_Cmd(ADC1, ENABLE);
	
	TIM_Cmd(TIM2, ENABLE);
	while (1)
	{
		while (ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC) == RESET);
		ADC_ClearFlag(ADC1, ADC_FLAG_JSTRT | ADC_FLAG_EOC | ADC_FLAG_JEOC);
		printf(" %d %d %d %d (ADC1->SR=0x%02x)\n", ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1), 
		  ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2), ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_3), 
		  ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_4), ADC1->SR);
		
		if (i == 4)
		{
			ADC_InjectedSequencerLengthConfig(ADC1, 4);
			ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
		}
		if (i != 5)
			i++;
	}
}

void TIM2_IRQHandler(void)
{
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	printf("*");
}


【注意】

1. 规则通道组和注入通道组不能同时打开间断模式。

2. 自动注入(JAUTO)模式不能和间断模式同时使用。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

巨大八爪鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值