一,ADC
模擬信號
什么是模擬信號?主要是與離散的數字信號相對的連續的信號。模擬信號分布於自然界的各個角落,如每天溫度的變化,
而數字信號是人為的抽象出來的在時間上不連續的信號。電學上的模擬信號是主要是指幅度和相位都連續的電信號,
此信號可以被模擬電路進行各種運算,如放大,相加,相乘等。模擬信號是指用連續變化的物理量表示的信息,
其信號的幅度,或頻率,或相位隨時間作連續變化,如目前廣播的聲音信號,或圖像信號等 。常見的模擬信號有正弦波、 調幅波、 阻尼震盪波、 指數衰減波 。
數字信號
什么是數字信號?
數字信號指幅度的取值是離散的,幅值表示被限制在有限個數值之內。 二進制碼就是一種數字信號。
二進制碼受噪聲的影響小,易於有數字電路進行處理,所以得到了廣泛的應用
優點:
抗干擾能力強、無噪聲積累
在模擬通信中,為了提高信噪比,需要在信號傳輸過程中及時對衰減的傳輸信號進行放大,信號在傳輸過程中不可避免地疊加上的噪聲也被同時放大。
隨着傳輸距離的增加,噪聲累積越來越多,以致使傳輸質量嚴重惡化。
對於數字通信,由於數字信號的幅值為有限個離散值(通常取兩個幅值),在傳輸過程中雖然也受到噪聲的干擾,但當信噪比惡化到一定程度時,
即在適當的距離采用判決再生的方法,再生成沒有噪聲干擾的和原發送端一樣的數字信號,所以可實現長距離高質量的傳輸。
便於加密處理
信息傳輸的安全性和保密性越來越重要,數字通信的加密處理的比模擬通信容易得多,以話音信號為例,經過數字變換后的信號可用簡單的數字邏輯運算進行加密、解密處理。
便於存儲、處理和交換
數字通信的信號形式和計算機所用信號一致,都是二進制代碼,因此便於與計算機聯網,也便於用計算機對數字信號進行存儲、處理和交換,
可使通信網的管理、維護實現自動化、智能化。
設備便於集成化、微型
數字通信采用時分多路復用,不需要體積較大的濾波器。設備中大部分電路是數字電路,可用大規模和超大規模集成電路實現,因此體積小、功耗低。
便於構成綜合數字網和綜合業務數字網
采用數字傳輸方式,可以通過程控數字交換設備進行數字交換,以實現傳輸和交換的綜合。
另外,電話業務和各種非話業務都可以實現數字化,構成綜合業務數字網。
占用信道頻帶較寬
一路模擬電話的頻帶為4kHz帶寬,一路數字電話約占64kHz,這是模擬通信目前仍有生命力的主要原因。
隨着寬頻帶信道(光纜、數字微波)的大量利用(一對光纜可開通幾千路電話)以及數字信號處理技術的發展(可將一路數字電話的數碼率由64kb/s壓縮到32kb/s甚至更低的數碼率),
數字電話的帶寬問題已不是主要問題了
常用的數字信號編碼有不歸零(NRZ)編碼、 曼徹斯特(Manchester)編碼和差分曼徹斯特(Differential Manchester)編碼。
數字信號與模擬信號的轉化
模擬信號和數字信號之間可以相互轉換:模擬信號一般通過PCM脈碼調制(Pulse Code Modulation)方法量化為數字信號,
即讓模擬信號的不同幅度分別對應不同的二進制值,例如采用8位編碼可將模擬信號量化為2^8=256個量級,實用中常采取24位或30位編碼;
數字信號一般通過對載波進行移相(Phase Shift)的方法轉換為模擬信號。計算機、計算機局域網與城域網中均使用二進制數字信號,
目前在計算機廣域網中實際傳送的則既有二進制數字信號,也有由數字信號轉換而得的模擬信號。但是更具應用發展前景的是數字信號。
PCM脈碼調制
脈沖編碼調制就是把一個時間連續,取值連續的模擬信號變換成時間離散,取值離散的數字信號后在信道中傳輸。
脈沖編碼調制就是對模擬信號先抽樣,再對樣值幅度量化, 編碼的過程。
抽樣: 就是對模擬信號進行周期性掃描,把時間上連續的信號變成時間上離散的信號。
該模擬信號經過抽樣后還應當包含原信號中所有信息,也就是說能無失真的恢復原模擬信號。
量化: 就是把經過抽樣得到的瞬時值將其幅度離散,即用一組規定的電平,把瞬時抽樣值用最接近的電平值來表示,通常是用二進制表示。
編碼: 就是用一組二進制碼組來表示每一個有固定電平的量化值。然而,實際上量化是在編碼過程中同時完成的,故編碼過程也稱為模/數變換,可記作A/D。
STM32F4手冊
如何知道當前是使用哪個ADC硬件,同時使用哪個通道?
PA5/ADC12_IN5,表示PA5引腳支持ADC1或ADC2進行掃描,使用通道是第五個輸入通道
存儲對齊方式
通過ADC硬件獲取到的結果值,為什么還得要進行轉換為電壓值,依據是什么?
通過調整可調電阻實現燈光亮度的變化
int main(void) { uint32_t adc_val,adc_vol,pwm_cmp=0;; //系統時鍾的時鍾源=168MHz/8=21MHz SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //串口初始化,波特率為115200bps usart1_init(115200); //adc初始化,12位精度 adc_init(); //定時器14初始化為PWM,使用PWM通道1,當前頻率為100Hz tim14_init(); while(1) { /* Start ADC Software Conversion ,啟動ADC*/ ADC_SoftwareStartConv(ADC1); //等待轉換結束 while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //獲取ADC轉換后的數值 adc_val=ADC_GetConversionValue(ADC1); //將ADC的數值轉換為電壓值 adc_vol = adc_val *3300/4095; printf("vol=%dmv\r\n",adc_vol); //設置比較值 pwm_cmp = adc_vol*100/3300 ; TIM_SetCompare1(TIM14,pwm_cmp); printf("pwm compare=%d\r\n",pwm_cmp); delay_ms(500); } }

void tim14_init(void) { /* GPIOF clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); /* GPIOF Configuration: TIM14 CH1 (PF9) */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //復用功能,使用引腳的第二功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(GPIOF, &GPIO_InitStructure); /* Connect TIM pins to AF9 */ GPIO_PinAFConfig(GPIOF, GPIO_PinSource9, GPIO_AF_TIM14); /* TIM14 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE); /* Time base configuration,100Hz*/ TIM_TimeBaseStructure.TIM_Period = (10000/100)-1; //定時計數值,100Hz TIM_TimeBaseStructure.TIM_Prescaler = 8400; //預分頻值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //再次進行1分頻 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數 TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure); /* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM14, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable); //自動重載初值,不斷輸出PWM脈沖 TIM_ARRPreloadConfig(TIM14, ENABLE); //自動重載使能 /* TIM14 enable counter */ TIM_Cmd(TIM14, ENABLE); }

void adc_init(void) { //使能GPIOA的硬件時鍾 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能ADC1硬件時鍾 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); /* 配置ADC1通道5為模擬輸入引腳 */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //第5號引腳 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //引腳設置為模擬輸入,能夠識別更加廣范圍的電平(0V~3.3V任何電壓) GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(GPIOA, &GPIO_InitStructure); /* ADC常規的初始化 */ ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //獨立模式,在當前的通道5只采用1個ADC硬件進行工作 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; //ADC硬件的工作時鍾= APB2(84MHz)/2=42MHz ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA //ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //如果采用多個ADC對某一個通道進行采樣的時候,才需要設置 ADC_CommonInit(&ADC_CommonInitStructure); /* ADC1初始化*/ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度,非常重要 ADC_InitStructure.ADC_ScanConvMode = DISABLE; //因不需要DMA,則不需要使用自動掃描模式。當前使用軟件觸發一次,則掃描一次 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模擬數字轉換器一直工作 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止觸發檢測,不需要外部引腳電平識別來讓ADC硬件工作 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右對齊 ADC_InitStructure.ADC_NbrOfConversion = 1; //執行一次轉換結果 ADC_Init(ADC1, &ADC_InitStructure); /* 指定ADC1常規通道5的采樣時間,采樣時間 = 3個ADC時鍾時間 = 3* (1/42MHz)*/ ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_3Cycles); /* Enable ADC1,使能ADC1 */ ADC_Cmd(ADC1, ENABLE); }
光敏電阻
原理
光敏電阻的工作原理:光照時,電阻很小;無光照時,電阻很大。光照越強,電阻越小;光照停止,電阻又恢復原值。
應用案例:手機自動亮度,根據的光照的強度來動態調整手機屏幕的亮度。
二,DAC

#include <stdio.h> #include "stm32f4xx.h" #include "sys.h" static GPIO_InitTypeDef GPIO_InitStructure; static NVIC_InitTypeDef NVIC_InitStructure; static USART_InitTypeDef USART_InitStructure; static ADC_InitTypeDef ADC_InitStructure; static ADC_CommonInitTypeDef ADC_CommonInitStructure; static DAC_InitTypeDef DAC_InitStructure; //重定義fputc函數 int fputc(int ch, FILE *f) { USART_SendData(USART1,ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET); return ch; } void delay_us(uint32_t nus) { uint32_t temp; SysTick->LOAD =SystemCoreClock/8/1000000*nus; //時間加載 SysTick->VAL =0x00; //清空計數器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //使能滴答定時器開始倒數 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待時間到達 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //關閉計數器 SysTick->VAL =0X00; //清空計數器 } void delay_ms(uint16_t nms) { uint32_t temp; SysTick->LOAD=SystemCoreClock/8/1000*nms; //時間加載(SysTick->LOAD為24bit) SysTick->VAL =0x00; //清空計數器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //能滴答定時器開始倒數 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待時間到達 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //關閉計數器 SysTick->VAL =0X00; //清空計數器 } void USART1_Init(uint32_t baud) { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能GPIOA時鍾 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); //使能USART1時鍾 //串口1對應引腳復用映射 GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //GPIOA9復用為USART1 GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); //GPIOA10復用為USART1 //USART1端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9與GPIOA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //復用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽復用輸出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10 //USART1 初始化設置 USART_InitStructure.USART_BaudRate = baud; //波特率設置 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字長為8位數據格式 USART_InitStructure.USART_StopBits = USART_StopBits_1; //一個停止位 USART_InitStructure.USART_Parity = USART_Parity_No; //無奇偶校驗位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //無硬件數據流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式 USART_Init(USART1, &USART_InitStructure); //初始化串口 USART_Cmd(USART1, ENABLE); //使能串口1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟相關中斷 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //串口1中斷通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //搶占優先級3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優先級3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器 } void adc_init(void) { /* Enable ADC,and GPIO clocks ,使能ADC與GPIO的時鍾*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); /* Configure ADC1 Channel3 pin as analog input ,配置ADC1的通道3作為模擬輸入引腳*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模擬輸入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(GPIOA, &GPIO_InitStructure); /* ADC Common Init ,ADC的常規初始化*/ ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //獨立模式,在當前的通道5只能使用ADC1進行掃描,不需要二重/三重掃描 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; //ADC硬件的工作時鍾=APB2(84MHz)/2=42MHz ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA //ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; //如果采用二重/三重采樣的時候,才需要設置 ADC_CommonInit(&ADC_CommonInitStructure); /* ADC1 Init ,ADC1初始化*/ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度,非常重要[*] ADC_InitStructure.ADC_ScanConvMode = DISABLE; //因不需要多重采樣,則不需要連續掃描 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模擬數字轉換器一直工作 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止外部觸發進行模擬轉換工作 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //存儲的數據以右對齊的方式進行存儲[*] ADC_InitStructure.ADC_NbrOfConversion = 1; //執行一次轉換結果 ADC_Init(ADC1, &ADC_InitStructure); /* ADC1 regular channel3 configuration,指定ADC1常規通道3的采樣時間,使用3個ADC時鍾時間*/ ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 1, ADC_SampleTime_3Cycles); /* Enable ADC1,使能ADC1 */ ADC_Cmd(ADC1, ENABLE); } void dac_init(void) { /* DAC Periph clock enable ,DAC硬件時鍾使能*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE); //GPIOA的硬件時鍾使能 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //設置PA4引腳為模擬信號引腳 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); /* DAC channel1 Configuration ,DAC通道1的初始化*/ DAC_InitStructure.DAC_Trigger = DAC_Trigger_None; //不依賴其他定時器來觸發DAC硬件的輸出,通過軟件觸發輸出電壓值 DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None; //不需要輸出任何的波形,電壓值是由程序來決定 DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //輸出使能 DAC_Init(DAC_Channel_1, &DAC_InitStructure); /* Enable DAC Channel2 使能DAC的通道1*/ DAC_Cmd(DAC_Channel_1, ENABLE); /* Set DAC channel1 DHR12RD register,設置DAC的輸出值以12位的右對齊方式,也就是設置DAC輸出的電壓值, */ DAC_SetChannel1Data(DAC_Align_12b_R, 0); } int main(void) { uint32_t adc_vol=0; uint32_t adc_val=0; uint32_t dac_vol=0; //系統定時器初始化,時鍾源來自HCLK,且進行8分頻, //系統定時器時鍾頻率=168MHz/8=21MHz SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //設置中斷優先級分組2 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //串口1,波特率115200bps,開啟接收中斷 USART1_Init(115200); //adc初始化 adc_init(); //dac初始化 dac_init(); printf("This is adc dac test\r\n"); while(1) { dac_vol+=100; //使用DAC1輸出自定義的電壓值 DAC_SetChannel1Data(DAC_Align_12b_R, dac_vol*4095/3300); printf("dac vol=%dmv\r\n",dac_vol); if(dac_vol >=3300) dac_vol=0; //延時 delay_ms(500); //啟動ADC1進行轉換 ADC_SoftwareStartConv(ADC1); //等待轉換結束 while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET); //獲取結果值 adc_val=ADC_GetConversionValue(ADC1); //將結果值轉換為電壓值 adc_vol = adc_val *3300/0xFFF; //將電壓值進行打印輸出 printf("adc vol=%dmv\r\n",adc_vol); //延時 delay_ms(500); } }