STM32F103RC微控制器ADC1使用TIM1自動觸發注入通道組的AD轉換
版權宣告:本文為博主原創文章,歡迎轉載 https://blog.csdn.net/ZLK1214/article/details/77746783
注意:ADC外設最大允許的時鐘頻率為14MHz,開啟ADC外設前必須先配置好分頻係數!
72MHz / 6 = 12MHz,轉換速率為每個1.17μs。
【程式】
-
#include <stdio.h>
-
#include <stm32f10x.h>
-
uint8_t injected_count = 0; // 轉換的注入通道組數
-
uint8_t last_pos; // 定時器1停止時剩餘規則通道個數(DMA位置)
-
uint16_t injected_result[4][5]; // 注入通道轉換結果, 包括DMA位置
-
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, j;
-
uint16_t result[10][16]; // 規則通道轉換結果
-
// 開啟外設時鐘
-
RCC->CFGR |= RCC_CFGR_ADCPRE_1; // ADC時鐘設為12MHz, 最大允許時鐘為14MHz
-
RCC->AHBENR = RCC_AHBENR_DMA1EN;
-
RCC->APB2ENR = RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN;
-
GPIOA->CRH = 0x444444bf; // 串列埠傳送端設為複用推輓50MHz輸出, TIM1輸出比較埠1設為複用開漏50MHz輸出
-
GPIOA->CRL = 0x00000004; // ADC1通道1~7設為模擬
-
GPIOB->CRL = 0x44444400; // ADC1通道8~9設為模擬
-
TIM1->ARR = 15; // PWM週期: 16us
-
TIM1->PSC = 71; // 計時頻率: 1MHz
-
TIM1->RCR = 3; // 產生4個上升沿訊號
-
TIM1->CR1 = TIM_CR1_OPM | TIM_CR1_URS; // 定時器自動停止; UG不置位UIF
-
TIM1->EGR = TIM_EGR_UG; // 裝入ARR, PSC和RCR暫存器的值
-
TIM1->DIER = TIM_DIER_UIE; // 當定時器自動停止時觸發中斷
-
NVIC_EnableIRQ(TIM1_UP_IRQn);
-
TIM1->CR2 = TIM_CR2_MMS_2; // TRGO=OC1REF, 用於觸發注入通道轉換
-
TIM1->BDTR = TIM_BDTR_MOE; // 開啟輸出總開關
-
TIM1->CCR1 = 11; // 上升沿的產生位置
-
TIM1->CCMR1 = TIM_CCMR1_OC1M; // PWM mode 2
-
TIM1->CCER = TIM_CCER_CC1E; // 開啟輸出比較通道1
-
// 規則通道和注入通道序列
-
// SQ10: TS, SQ16: VREF
-
ADC1->SQR1 = ADC_SQR1_L | (ADC_SQR1_SQ16_4 | ADC_SQR1_SQ16_0) | (ADC_SQR1_SQ15_2 | ADC_SQR1_SQ15_0) | ADC_SQR1_SQ14_2 | (ADC_SQR1_SQ13_1 | ADC_SQR1_SQ13_0);
-
ADC1->SQR2 = ADC_SQR2_SQ12_1 | ADC_SQR2_SQ11_0 | 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->JSQR = ADC_JSQR_JL | ADC_JSQR_JSQ4_2 | (ADC_JSQR_JSQ3_1 | ADC_JSQR_JSQ3_0) | ADC_JSQR_JSQ2_1 | ADC_JSQR_JSQ1_0;
-
ADC1->CR1 = ADC_CR1_SCAN | ADC_CR1_JEOCIE; // 規則通道組配置為多通道模式(必須用DMA收集資料); 當注入通道組轉換完畢時觸發中斷
-
ADC1->CR2 = ADC_CR2_TSVREFE | ADC_CR2_JEXTTRIG | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON; // 開啟16~17通道; 允許外部事件注入; 規則通道組迴圈轉換; 開啟ADC1
-
NVIC_EnableIRQ(ADC1_2_IRQn);
-
USART1->BRR = 0x271; // 波特率: 115200
-
USART1->CR1 = USART_CR1_UE | USART_CR1_TE; // 允許傳送
-
// 配置ADC1_DMA
-
DMA1_Channel1->CMAR = (uint32_t)result[0];
-
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
-
DMA1_Channel1->CNDTR = 160;
-
DMA1_Channel1->CCR = DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0 | DMA_CCR1_MINC | DMA_CCR1_EN;
-
ADC1->CR2 = ADC1->CR2; // 開始轉換規則通道
-
TIM1->CR1 |= TIM_CR1_CEN; // 開啟定時器1
-
while ((DMA1->ISR & DMA_ISR_TCIF1) == 0) // 若DMA正在搬運資料
-
{
-
if (DMA1_Channel1->CNDTR < 16 && (ADC1->CR2 & ADC_CR2_CONT)) // 轉換最後一組時關閉CONT迴圈模式, 以免產生新資料使EOC不為0
-
ADC1->CR2 &= ~ADC_CR2_CONT;
-
}
-
// 顯示定時器1自動關閉時DMA的位置
-
if ((TIM1->CR1 & TIM_CR1_CEN) == 0)
-
printf("TIM1 stopped! CNDTR=%d\n", last_pos);
-
// 顯示規則通道的資料
-
for (i = 0; i < 10; i++)
-
{
-
printf("[Regular %d]", i + 1);
-
for (j = 0; j < 16; j++)
-
printf(" %d", result[i][j]);
-
printf("\n");
-
}
-
// 顯示注入通道的資料
-
for (i = 0; i < injected_count; i++)
-
{
-
printf("[Injected %d]", i + 1);
-
for (j = 1; j <= 4; j++)
-
printf(" %d", injected_result[i][j]);
-
printf(" (CNDTR=%d)\n", injected_result[i][0]);
-
}
-
ADC1->SR &= ~ADC_SR_STRT;
-
printf("ADC1->SR=0x%02x\n", ADC1->SR);
-
// 等待USART1傳送完畢並關閉外設
-
while ((USART1->SR & USART_SR_TC) == 0);
-
USART1->CR1 = 0;
-
ADC1->CR2 = 0;
-
// 進入Stop低功耗模式
-
SCB->SCR = SCB_SCR_SLEEPDEEP;
-
while (1)
-
__WFI();
-
}
-
// 注入通道組轉換完成時觸發的中斷
-
void ADC1_2_IRQHandler(void)
-
{
-
ADC1->SR &= ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_JSTRT);
-
injected_result[injected_count][0] = DMA1_Channel1->CNDTR;
-
injected_result[injected_count][1] = ADC1->JDR1;
-
injected_result[injected_count][2] = ADC1->JDR2;
-
injected_result[injected_count][3] = ADC1->JDR3;
-
injected_result[injected_count][4] = ADC1->JDR4;
-
injected_count++;
-
}
-
void TIM1_UP_IRQHandler(void)
-
{
-
TIM1->SR &= ~TIM_SR_UIF; // 清除中斷標誌
-
last_pos = DMA1_Channel1->CNDTR; // 記錄DMA的位置
-
ADC1->CR2 &= ~ADC_CR2_JEXTTRIG; // 禁止外部觸發注入通道組的轉換
-
TIM1->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0; // 熄滅PA8上的LED燈
-
// 不能在這裡執行printf, 否則CONT位將無法及時清除而產生新資料
-
}
【執行結果】
-
TIM1 stopped! CNDTR=122
-
[Regular 1] 1638 1714 598 225 1566 3085 3572 1834 3469 1576 1636 1716 598 225 1565 1770
-
[Regular 2] 1639 1714 597 224 1568 3084 3569 1832 3468 2157 1638 1715 597 225 1564 1772
-
[Regular 3] 1638 1713 597 224 1567 3084 3570 1833 3469 2159 1638 1713 597 225 1567 1773
-
[Regular 4] 1637 1714 597 225 1567 3085 3571 1834 3469 2158 1639 1715 598 224 1566 1771
-
[Regular 5] 1638 1715 598 224 1566 3087 3568 1834 3470 2157 1638 1714 596 223 1569 1766
-
[Regular 6] 1638 1714 598 224 1567 3085 3570 1832 3470 2158 1638 1715 598 225 1567 1773
-
[Regular 7] 1638 1715 597 223 1565 3086 3569 1832 3469 2158 1637 1714 598 224 1566 1772
-
[Regular 8] 1638 1714 598 225 1565 3085 3568 1833 3469 2158 1638 1715 598 225 1566 1770
-
[Regular 9] 1638 1714 598 225 1568 3084 3571 1833 3470 2158 1638 1714 597 224 1565 1771
-
[Regular 10] 1638 1714 597 224 1566 3084 3569 1833 3469 2158 1638 1713 598 224 1568 1765
-
[Injected 1] 1636 1714 597 224 (CNDTR=151)
-
[Injected 2] 1637 1715 597 224 (CNDTR=142)
-
[Injected 3] 1636 1714 596 224 (CNDTR=133)
-
[Injected 4] 1638 1714 597 225 (CNDTR=124)
-
ADC1->SR=0x00
在上面的程式中,TIM1的計數方向為向上計數,每次比較匹配時都觸發一次注入。事件源為TIM1_TRGO事件,JEXTSEL=000。
如果計數方向既向上又向下,則只有向上的那一次比較匹配才能觸發注入,向下的那一次無效,因為只有TRGO=OCxREF訊號的上升沿能夠觸發ADC注入。
對於TIMn_TRGO類事件,若TRGO=OCxREF,則只有OCxREF的上升沿才能觸發注入,與CCxP的值無關。
對於TIMn_CCx類事件,若CCxP=0,則是OCxREF的上升沿觸發注入;若CCxP=1,則是OCxREF的下降沿觸發注入。對應的GPIO口輸出的電平為OCxREF ^ CCxP。
當SCAN=0時,L和JL的值無效,ADC始終只轉換序列中的第一個通道。
當SCAN=1時,ADC轉換組中所有的通道,通道的個數由L或JL指定。整個通道組轉換完成時EOC才置1,此時DR中的內容為最後一個通道的資料,讀DR暫存器後EOC自動清零。所以只要SCAN=1,規則通道的資料就只能由DMA來採集。如果打開了EOCIE中斷,則在中斷中無需手動清除EOC標誌。因為DMA讀取最後一個通道的資料後,EOC會自動清零。
CONT(迴圈模式)只對規則通道有效,對注入通道無效。
如果JAUTO=1,則注入通道組的轉換不需要由外部訊號觸發。規則通道組轉換完成後將會自動轉換注入通道組。
-
#include <stdio.h>
-
#include <stm32f10x.h>
-
uint8_t injected_count = 0; // 轉換的注入通道組數
-
uint16_t injected_result[10][5]; // 注入通道轉換結果, 包括DMA位置
-
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;
-
}
-
int main(void)
-
{
-
uint8_t i, j;
-
uint16_t result[10][16]; // 規則通道轉換結果
-
// 開啟外設時鐘
-
RCC->CFGR |= RCC_CFGR_ADCPRE_1; // ADC時鐘設為12MHz, 最大允許時鐘為14MHz
-
RCC->AHBENR = RCC_AHBENR_DMA1EN;
-
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設為模擬
-
// 規則通道和注入通道序列
-
// SQ10: TS, SQ16: VREF
-
ADC1->SQR1 = ADC_SQR1_L | (ADC_SQR1_SQ16_4 | ADC_SQR1_SQ16_0) | (ADC_SQR1_SQ15_2 | ADC_SQR1_SQ15_0) | ADC_SQR1_SQ14_2 | (ADC_SQR1_SQ13_1 | ADC_SQR1_SQ13_0);
-
ADC1->SQR2 = ADC_SQR2_SQ12_1 | ADC_SQR2_SQ11_0 | 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->JSQR = ADC_JSQR_JL | ADC_JSQR_JSQ4_2 | (ADC_JSQR_JSQ3_1 | ADC_JSQR_JSQ3_0) | ADC_JSQR_JSQ2_1 | ADC_JSQR_JSQ1_0;
-
ADC1->CR1 = ADC_CR1_JAUTO | ADC_CR1_SCAN | ADC_CR1_JEOCIE; // 規則通道組轉換完畢後自動開始轉換注入通道組; 規則通道組配置為多通道模式(必須用DMA收集資料); 當注入通道組轉換完畢時觸發中斷
-
ADC1->CR2 = ADC_CR2_TSVREFE | ADC_CR2_DMA | ADC_CR2_CONT | ADC_CR2_ADON; // 開啟16~17通道; 規則通道組迴圈轉換; 開啟ADC1
-
NVIC_EnableIRQ(ADC1_2_IRQn);
-
USART1->BRR = 0x271; // 波特率: 115200
-
USART1->CR1 = USART_CR1_UE | USART_CR1_TE; // 允許傳送
-
// 配置ADC1_DMA
-
DMA1_Channel1->CMAR = (uint32_t)result[0];
-
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
-
DMA1_Channel1->CNDTR = 160;
-
DMA1_Channel1->CCR = DMA_CCR1_MSIZE_0 | DMA_CCR1_PSIZE_0 | DMA_CCR1_MINC | DMA_CCR1_EN;
-
ADC1->CR2 = ADC1->CR2; // 開始轉換規則通道
-
while ((DMA1->ISR & DMA_ISR_TCIF1) == 0) // 若DMA正在搬運資料
-
{
-
if (DMA1_Channel1->CNDTR < 16 && (ADC1->CR2 & ADC_CR2_CONT)) // 轉換最後一組時關閉CONT迴圈模式, 以免產生新資料使EOC不為0
-
ADC1->CR2 &= ~ADC_CR2_CONT;
-
}
-
// 顯示規則通道的資料
-
for (i = 0; i < 10; i++)
-
{
-
printf("[Regular %d]", i + 1);
-
for (j = 0; j < 16; j++)
-
printf(" %d", result[i][j]);
-
printf("\n");
-
}
-
// 顯示注入通道的資料
-
for (i = 0; i < injected_count; i++)
-
{
-
printf("[Injected %d]", i + 1);
-
for (j = 1; j <= 4; j++)
-
printf(" %d", injected_result[i][j]);
-
printf(" (CNDTR=%d)\n", injected_result[i][0]);
-
}
-
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;
-
}
-
// 注入通道組轉換完成時觸發的中斷
-
void ADC1_2_IRQHandler(void)
-
{
-
ADC1->SR &= ~(ADC_SR_EOC | ADC_SR_JEOC | ADC_SR_JSTRT);
-
injected_result[injected_count][0] = DMA1_Channel1->CNDTR;
-
injected_result[injected_count][1] = ADC1->JDR1;
-
injected_result[injected_count][2] = ADC1->JDR2;
-
injected_result[injected_count][3] = ADC1->JDR3;
-
injected_result[injected_count][4] = ADC1->JDR4;
-
injected_count++;
-
}
【執行結果】
-
[Regular 1] 1636 1713 597 224 1565 3084 3570 1833 3471 2083 1638 1713 597 224 1565 1772
-
[Regular 2] 1637 1713 597 224 1566 3084 3569 1832 3470 2158 1637 1714 597 223 1566 1773
-
[Regular 3] 1639 1713 597 222 1568 3084 3572 1833 3467 2157 1636 1713 596 223 1564 1771
-
[Regular 4] 1637 1713 596 223 1565 3085 3569 1833 3470 2158 1638 1713 597 224 1566 1771
-
[Regular 5] 1637 1713 596 225 1566 3084 3570 1833 3470 2158 1637 1713 596 223 1567 1772
-
[Regular 6] 1637 1713 596 223 1567 3086 3569 1833 3469 2157 1637 1714 595 223 1565 1771
-
[Regular 7] 1637 1713 597 224 1565 3086 3568 1834 3470 2158 1637 1714 597 225 1566 1771
-
[Regular 8] 1637 1713 596 225 1565 3085 3569 1832 3470 2158 1637 1713 597 223 1567 1774
-
[Regular 9] 1637 1713 596 223 1567 3084 3569 1832 3470 2158 1638 1713 596 224 1565 1771
-
[Regular 10] 1637 1713 597 224 1564 3085 3569 1833 3470 2158 1637 1713 597 224 1565 1771
-
[Injected 1] 1638 1714 597 224 (CNDTR=144)
-
[Injected 2] 1637 1713 596 224 (CNDTR=128)
-
[Injected 3] 1637 1715 596 223 (CNDTR=112)
-
[Injected 4] 1638 1714 595 223 (CNDTR=96)
-
[Injected 5] 1637 1713 597 223 (CNDTR=80)
-
[Injected 6] 1637 1713 597 223 (CNDTR=64)
-
[Injected 7] 1638 1715 597 224 (CNDTR=48)
-
[Injected 8] 1638 1712 596 222 (CNDTR=32)
-
[Injected 9] 1637 1713 597 222 (CNDTR=16)
-
[Injected 10] 1637 1714 597 226 (CNDTR=0)
-
ADC1->SR=0x00