STM32F103RC单片机ADC中的间断模式(Discontinous mode)的使用
时间:2023-11-08 07:07:02
对于规则通道组,每个通道在间歇模式下转换,EOC位置一次。所以不需要像。SCAN必须采用这种模式DMA处理数据。
当DISCEN=1点打开间断模式,DISCNUM指定每次转换的通道个数,范围为1~8。
在以下程序中,DISCNUM=每次转换4个通道。L=1001,共有10个通道。
【程序1】
#include #include 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设为模拟, PA待机唤醒按钮设置为带下拉电阻输入 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=0xx\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 #include 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设为模拟, PA待机唤醒按钮设置为带下拉电阻输入 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=0xx)\n", ADC1->JDR1, ADC1->JDR2, ADC1-&g;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
#include
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)模式不能和间断模式同时使用。