1. 程式人生 > >STM32F103RC微控制器ADC中的間斷模式(Discontinous mode)的使用

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)模式不能和間斷模式同時使用。

相關推薦

STM32F103RC微控制器ADC間斷模式Discontinous mode的使用

對於規則通道組,間斷模式下每轉換一個通道,EOC就會置位一次。因此不必像SCAN模式那樣必須採用DMA來搬運資料。 當DISCEN=1時開啟間斷模式,DISCNUM指定每次轉換的通道個數,範圍為1~8。 在下面的程式中,DISCNUM=011,每次轉換4個通道。L=1001

BigDecimal 舍入模式Rounding mode介紹

BigDecimal 舍入模式(Rounding mode)介紹 什麼樣的經歷,才能領悟成為架構師? >>>    1 RoundingMode介紹 package java.math; publ

Linux kernel boot process——從真實模式real mode到保護模式protected mode,再到分頁paging

        本文簡要介紹X86-32架構下的Linux kernel被boot loader(如grub)載入到記憶體後,如何從最初的真實模式,切換到保護模式,並開啟分頁機制。本文不涉及boot loader如何將核心載入到記憶體,因為這是boot loader的事,跟

Linux kernel boot process——從真實模式real mode到保護模式protected mode,再到分頁paging .

       本文簡要介紹X86-32架構下的Linux kernel被boot loader(如grub)載入到記憶體後,如何從最初的真實模式,切換到保護模式,並開啟分頁機制。本文不涉及boot loader如何將核心載入到記憶體,因為這是boot loader的事,跟核

有關ie瀏覽器怪異模式Quirks Mode對 HTML 頁面的影響的解決辦法

一: 把 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 改為 <!DOCT

怪異模式Quirks Mode對 HTML 頁面的影響

轉載自:http://www.ibm.com/developerworks/cn/web/1310_shatao_quirks/#ibm-pcon 本文主要描述 Quirks Mode 和 Standards Mode 這兩種文件模式渲染頁面時的差異由來,並且給出一些詳細

HDFS block丟失過多進入安全模式safe mode的解決方法

HDFS block丟失過多進入安全模式(Safe mode)的解決方法 背景及現象描述(Background and Symptom)  因磁碟空間不足,記憶體不足,系統掉電等其他原因導致dataNode datablock丟失,出現如下類似日誌: The n

【設計模式】使用unity實現代理模式Proxy mode

閱讀《大話設計模式》後,自己根據unity的特性寫的一個簡單demo,如有不妥之處,歡迎評論糾正.... 先貼上書中的程式碼: using System; using System.Coll

Android開發——Activity的四種載入模式launch mode

  上一篇部落格我們說了Activity的生命週期,這次我們要說的是Activity的四種載入模式。我們可以在manifest.xml中每個activity的launch mode屬性上設定。   Activity的載入模式?什麼意思?Activity的載入模式,就負責管理

Android 觸控模式Touch Mode

什麼是焦點? 在非觸屏手機時代或電腦上,我們通常需要用鍵盤、 滑鼠、軌跡球(trackball)與介面進行互動,當互動的時候必須使目標控制元件獲得焦點(比如高亮起來),這樣使用者才會注意到是什麼控制元件接受輸入。而如果是在觸屏時代,使用者可以直接用手指點選控制

筆記本外接顯示器,顯示器頻繁進入省電模式save mode

1,首先看看設定的睡眠時間,比如你如果設定為1分鐘,那肯定容易睡眠。(我設定的不睡眠,但還是頻繁進入省電模式) 2,轉接線沒插緊導致的。我的就是這個問題,轉接線那裡有點鬆,導致顯示器頻繁識別不到訊號,識別不到訊號就會進入省電模式。我重新把轉接線插緊,就解決這個

怪異模式Quirks Mode對 HTML 頁面的影響w

在看權威指南,偶然間看到這個的一點介紹,感覺不錯。特此分享 Quirks Mode 概述 定義 什麼是 Quirks Mode? 簡單來說,Quirks Mode 就是瀏覽器為了相容很早之前針對舊版本瀏覽器設計、並未嚴格遵循 W3C 標準的網頁而產生的一種頁面

強悍的 vim —— 可視模式visual mode

指定 大神 con ont vim clas 修改 .net targe 進入可視模式的最重要也最直接的目的即是選中區域(ctrl + v:指定列,shift + v:指定行)。 1. 進入可視模式的方法 (1)處理列,將光標移動到想要修改的列(的開始或終止區域

[UWP]用畫中畫模式CompactOverlay Mode讓用總在最前端顯示

1. 什麼是,以及怎麼用畫中畫 Windows 10 Creators Update以後UWP提供了一個新的檢視模式CompactOverlay,中文翻譯成 緊湊的覆蓋層?反正大部分時間我們都會稱它為畫中畫模式。 上圖中右上角即為進入畫中畫模式的微軟“電影和電視”應用。 可以呼叫ApplicationVi

android開發學習 ------- 【轉】 android的單例模式 詳解

lan post tail -- and 使用 href details android開發 https://blog.csdn.net/u011418943/article/details/60139644 這篇文章 前因後果 都說出來了 ,值得學習。 htt

openstack虛機修復模式類似PE

openstack救援 pe 修復當虛機系統不能啟動時,在openstack環境的修復辦法:1、使用nova rescue <server> 會從虛機初始鏡像啟動實例,將實例的原有磁盤會做為second硬盤2、啟動後將第二硬盤聯機就可以修復了。3、修復系統完成後,需要將虛機從rescue恢復到正

在 js 應用 訂閱釋出模式subscrib/public

什麼是釋出-訂閱者模式 我們在使用釋出-訂閱者模式之前,先了解什麼是釋出-訂閱者模式。簡單來說,釋出訂閱者模式就是一種一對多的依賴關係。多個訂閱者(一般是註冊的函式)同時監聽同一個資料物件,當這個資料物件發生變化的時候會執行一個釋出事件,通過這個釋出事件會通知到所有的訂閱者,使它

設計模式(9)----- 補充spring的工廠設計模式手寫

package com.DesignPatterns.ad.factory6; public interface BeanFactory { Object getBean(String id); }     package com.DesignPattern

Java設計模式:建造者模式

介紹 今天我們將研究java中的Builder模式。Builder 設計模式是一種創造性的設計模式,如工廠模式和抽象工廠模式。 當Object包含許多屬性時,引入了Builder模式來解決Factory和Abstract Factory設計模式的一些問題。 當Object包含許多屬性時,Factory和Abs

設計模式--spring原始碼使用策略模式Strategy Pattern

策略模式(Strategy Pattern)中體現了兩個非常基本的面向物件設計的基本原則:封裝變化的概念;程式設計中使用介面,而不是對介面實現。策略模式的定義如下: 定義一組演算法,將每個演算法都封裝起來,並且使它們之間可以互換。策略模式使這些演算法在客戶端呼叫它們的時