对于规则通道组,间断模式下每转换一个通道,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)模式不能和间断模式同时使用。