 |
【實驗時用到的程序】 #include <stm32f10x.h>
const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
void delay(void) { uint16_t i; for (i = 0; i < 20000; i++); }
void ser_in(uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { GPIO_ResetBits(GPIOB, GPIO_Pin_9); // SCLK=>PB9 if (data & 0x80) GPIO_SetBits(GPIOB, GPIO_Pin_7); // DIO=>PB7 else GPIO_ResetBits(GPIOB, GPIO_Pin_7); data <<= 1; GPIO_SetBits(GPIOB, GPIO_Pin_9); } }
void par_out(void) { GPIO_ResetBits(GPIOB, GPIO_Pin_8); // RCLK=>PB8 GPIO_SetBits(GPIOB, GPIO_Pin_8); }
void seg_scan(void) { uint8_t numbuf = TIM_GetCounter(TIM2); uint8_t i; for (i = 0; i < 2; i++) // 只顯示兩位數字 { ser_in(seg8[numbuf % 10]); ser_in(1 << i); par_out(); delay(); numbuf /= 10; } }
int main(void) { GPIO_InitTypeDef gpio; TIM_ICInitTypeDef timic; TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // 時鐘輸入引腳配置 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &gpio); // 數碼管掃描管腳配置 gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &gpio); // 配置定時器的計數範圍 TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Down; // 向下計數 tim.TIM_Period = 99; // 最大計數值 tim.TIM_Prescaler = 0; // 不分頻 TIM_TimeBaseInit(TIM2, &tim); // 配置定時器2的通道1 timic.TIM_Channel = TIM_Channel_1; timic.TIM_ICFilter = 0; // 不消抖 timic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿觸發 (注意: 不能設為TIM_ICPolarity_BothEdge, 根本就沒有雙邊沿檢測功能) //timic.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 這個選項在外部時鐘模式中無效(輸入捕獲模式才會用到) timic.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI1->IC1=PA0 TIM_ICInit(TIM2, &timic); // 外部時鐘模式1 TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); // TRGI=TI1 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_External1); // 把時鐘信號設為TRGI TIM_Cmd(TIM2, ENABLE); while (1) seg_scan(); }
|
 |
【多諧振盪器的電路配置】 充電端接200Ω電阻和一個LED燈(電阻的大小決定輸出端上升沿的跳變時間)。 放電端接100kΩ電阻(電阻大小決定輸出端脈衝的寬度)。 這裏要注意的是,放電電阻不可以接的太大(如200kΩ),否則三極管會處於放大狀態,不僅使輸出的低電平電壓升高(如升高到2V甚至更高,單片機端將無法正確地檢測出輸出電平),而且很可能使整個多諧振盪器不能正常工作,甚至無法起振。 電容器為10μF。電容器靠近LED的那一端為正極,接三極管基極的那一端為負極。
當右輸出端接地時,兩個LED燈都亮,用萬用表測得左輸出端(三極管的集電極)的電壓為0.97V(輸出的低電平電壓)。這表明三極管導通時處於放大狀態,不過這並沒有什麼影響。 現在懸空右輸出端,將左輸出端接到單片機的PA0口上(定時器2的輸入通道1)。
當左LED燈熄滅時,左電容器通過充電小電阻快速充電,輸出端迅速從低電平(0.97V)升高到高電平(用萬用表測出來大約是3.4V,理論上是4.3V),並保持在高電平,直到右電容器放電完畢。 充電時間 = - 充電電阻大小 x 電容大小 x ln[(E - 末電壓) / (E - 初電壓)] 其中E為最大充電電壓,由於充電時電容器和充電電阻並聯在放電電阻兩端,而放電電阻又和右三極管的發射結是串聯的,發射結的導通電壓是0.7V,因此實際上電容器最大隻能充到E=5V-0.7V=4.3V 因此上升沿時間 = - 200 * 10 * 10^-6 * ln[(4.3-4.29) / (4.3-(-0.7))] (初電壓是-0.7V,因為放電完畢時電容器被反向充電到了0.7V,現在充電前必須先釋放掉這部分電壓) ≈ 12.43ms
當左LED燈亮起時,由於左三極管導通,左電容器的正極外接電壓<負極外接電壓,而正極內部電壓>負極內部電壓(因為之前已經充好電了),所以此時電源對該電容器進行反向充電(也就是放電),先從4.3V放到0V,再充到0.7V使右三極管導通,可以看作是從-4.3V充電到0.7V。這段時間左輸出端的電壓一直為0.97V,輸出低電平。右三極管的基極電壓從負壓(-4.3V)變到正壓(0.7V)。 持續的時間 = - 放電電阻大小 x 電容大小 x ln[(E - 末電壓) / (E - 初電壓)] = - 100 * 10^3 * 10 * 10^-6 * ln[(E - (+0.7))/(E - (-4.3))] (E=5V) ≈ 0.77s (計算時假定的是三極管是飽和導通。因為現在三極管是處於放大狀態,所以實際時間比這個時間要短。)
因此,左輸出端的上升沿出現在左LED燈熄滅的瞬間(電容器充電的速度非常快),程序中的定時器2就是對上升沿進行計數。
|
 |
編譯並下載單片機程序,實驗發現,雖然在程序中沒有開通道1的消抖功能,並且GPIO配置的是浮空輸入,但數碼管的數字顯示很穩定,僅當左LED燈熄滅時才減1(向下計數)。 把左LED燈短路,計時速度明顯加快,並且數碼管有時減2有時減1,振盪變得不穩定。 如果把兩個LED燈都短路,則數碼管每次減去的數毫無規律,抖動非常厲害。
|
 |
以下是多諧振盪器的電路圖。 本實驗把充電電阻R1和R4的阻值大小改成了200Ω,同時串聯了LED燈。

|
 |
在本次實驗中,Q1、Q2採用的三極管型號是9013NPN型。
|
 |
庫函數中有一個TIxExternalClockConfig函數可以更簡單地設置外部時鐘模式1: TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI1, TIM_ICPolarity_Rising, 0); 【示例程序】 int main(void) { GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // 時鐘輸入引腳配置 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &gpio); // 數碼管掃描管腳配置 gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &gpio); // 配置定時器的計數範圍 TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Down; // 向下計數 tim.TIM_Period = 99; // 最大計數值 tim.TIM_Prescaler = 0; // 不分頻 TIM_TimeBaseInit(TIM2, &tim); // 外部時鐘模式1 TIM_TIxExternalClockConfig(TIM2, TIM_TIxExternalCLK1Source_TI1, TIM_ICPolarity_Rising, 0); TIM_Cmd(TIM2, ENABLE); while (1) seg_scan(); }
|
 |
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); 這個函數也是配置的是模式1,但是使用的引腳卻是ETR引腳,即TRGI=ETR。 而之前的TIM_TIxExternalClockConfig函數使用的是TI1引腳,即TRGI=TI1FP1。 定時器2的ETR引腳和TI1引腳都是PA0,但對於定時器1來說兩個引腳是分開的。
|
 |
// 外部時鐘模式2 // 使用ETR (External Trigger)引腳作為外部時鐘信號 TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); TIM_Cmd(TIM2, ENABLE);
|
 |
現在多諧振盪器又出現了不穩定情況:有時候數碼管減的數字是2
|
 |
【實驗2:使用STM32的PWM輸入模式測量多諧振盪器的脈衝寬度】 #include <stm32f10x.h>
const uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
void delay(void) { uint16_t i; for (i = 0; i < 20000; i++); }
void ser_in(uint8_t data) { uint8_t i; for (i = 0; i < 8; i++) { GPIO_ResetBits(GPIOB, GPIO_Pin_9); // SCLK=>PB9 if (data & 0x80) GPIO_SetBits(GPIOB, GPIO_Pin_7); // DIO=>PB7 else GPIO_ResetBits(GPIOB, GPIO_Pin_7); data <<= 1; GPIO_SetBits(GPIOB, GPIO_Pin_9); } }
void par_out(void) { GPIO_ResetBits(GPIOB, GPIO_Pin_8); // RCLK=>PB8 GPIO_SetBits(GPIOB, GPIO_Pin_8); }
void seg_scan(void) { uint16_t numbuf; uint8_t i; if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1) == SET) numbuf = TIM_GetCapture1(TIM2); // 按鍵鬆開時顯示上次脈衝的總長度(單位: 秒) else numbuf = TIM_GetCapture2(TIM2); // 按鍵按下時顯示高電平的長度(單位: 秒) for (i = 0; i < 5; i++) { if (i == 4) ser_in(seg8[numbuf % 10] & 0x7f); // 顯示小數點 else ser_in(seg8[numbuf % 10]); ser_in(1 << i); par_out(); delay(); numbuf /= 10; } }
int main(void) { GPIO_InitTypeDef gpio; TIM_ICInitTypeDef timic; TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); // 時鐘輸入引腳配置 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOA, &gpio); // 數碼管掃描管腳配置 gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &gpio); // 按鍵管腳PC1配置 gpio.GPIO_Mode = GPIO_Mode_IPU; gpio.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOC, &gpio); // 配置定時器的計數範圍 TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Period = 0xffff; // 最大計數值 tim.TIM_Prescaler = 7199; // 7200分頻, 得到基準時鐘0.1ms TIM_TimeBaseInit(TIM2, &tim); // 配置定時器2的通道1 timic.TIM_Channel = TIM_Channel_1; timic.TIM_ICFilter = 0; // 不消抖 timic.TIM_ICPolarity = TIM_ICPolarity_Rising; // 通道1捕獲上升沿 timic.TIM_ICPrescaler = TIM_ICPSC_DIV1; timic.TIM_ICSelection = TIM_ICSelection_DirectTI; // TI1->IC1=PA0 TIM_ICInit(TIM2, &timic); // 配置定時器2的通道2 timic.TIM_Channel = TIM_Channel_2; timic.TIM_ICFilter = 0; timic.TIM_ICPolarity = TIM_ICPolarity_Falling; // 通道2捕獲下降沿 timic.TIM_ICPrescaler = TIM_ICPSC_DIV1; timic.TIM_ICSelection = TIM_ICSelection_IndirectTI; // TI2->IC1=PA0 TIM_ICInit(TIM2, &timic); TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); // TRGI=TI1 TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset); // 上升沿使定時器歸0 TIM_Cmd(TIM2, ENABLE); while (1) seg_scan(); }
|