久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 9447|回復: 0
收起左側

STM32學習筆記6:TIM模塊定時器

[復制鏈接]
ID:85109 發表于 2015-7-9 00:27 | 顯示全部樓層 |閱讀模式
                        TIM模塊定時器向上溢出 & 輸出比較
首先我們必須肯定ST公司的實力,也承認STM32的確是一款非常不錯的Cortex-M3核單片機,但是,他的手冊實在是讓人覺得無法理解,尤其是其中的TIM模塊,沒有條理可言,看了兩天幾乎還是不知所云,讓人很是郁悶。同時配套的固件庫的說明也很難和手冊上的寄存器對應起來,研究起來非常費勁!功能強大倒是真的,但至少也應該配套一個讓人看的明白的說明吧~~
兩天時間研究了STM32定時器的最最基礎的部分,把定時器最基礎的兩個功能實現了,余下的功能有待繼續學習。
首先有一點需要注意:FWLib固件庫目前的最新版應該是V2.0.x,V1.0.x版本固件庫中,TIM1模塊被獨立出來,調用的函數與其他定時器不同;在V2.0系列版本中,取消了TIM1.h,所有的TIM模塊統一調用TIM.h即可。網絡上流傳的各種代碼有許多是基于v1版本的固件庫,在移植到v2版本固件庫時,需要做些修改。本文的所有程序都是基于V2.0固件庫。
以下是定時器向上溢出示例代碼:
C語言: TIM1模塊產生向上溢出事件
//Step1.時鐘設置:啟動TIM1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//Step2.中斷NVIC設置:允許中斷,設置優先級
NVIC_InitStructure.NVIC_IRQChannel =TIM1_UP_IRQChannel;    //更新事件
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;   //搶占優先級0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =1;          //響應優先級1
NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE;             //允許中斷
NVIC_Init(&NVIC_InitStructure);                             //寫入設置
//Step3.TIM1模塊設置
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;

//TIM1 使用內部時鐘
//TIM_InternalClockConfig(TIM1);
//TIM1基本設置
//設置預分頻器分頻系數71,即APB2=72M, TIM1_CLK=72/72=1MHz
//TIM_Period(TIM1_ARR)=1000,計數器向上計數到1000后產生更新事件,計數值歸零
//向上計數模式
//TIM_RepetitionCounter(TIM1_RCR)=0,每次向上溢出都產生更新事件
TIM_BaseInitStructure.TIM_Period = 1000;
TIM_BaseInitStructure.TIM_Prescaler = 71;
TIM_BaseInitStructure.TIM_ClockDivision = 0;
TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure);
//清中斷,以免一啟用中斷后立即產生中斷
TIM_ClearFlag(TIM1, TIM_FLAG_Update);
//使能TIM1中斷源
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
//TIM1總開關:開啟
TIM_Cmd(TIM1, ENABLE);
}
//Step4.中斷服務子程序:
void TIM1_UP_IRQHandler(void)
{
GPIOC->ODR ^=(1<<4);                         //閃燈
TIM_ClearITPendingBit(TIM1, TIM_FLAG_Update); //清中斷
}
下面是輸出比較功能實現TIM1_CH1管腳輸出指定頻率的脈沖:
C語言: TIM1模塊實現輸出比較,自動翻轉并觸發中斷
//Step1.啟動TIM1,同時還要注意給相應功能管腳啟動時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//Step2. PA.8口設置為TIM1的OC1輸出口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Step3.使能TIM1的輸出比較匹配中斷
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//Step4. TIM模塊設置
void TIM_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    //TIM1基本計數器設置
    TIM_BaseInitStructure.TIM_Period =0xffff;                      //這里必須是65535
    TIM_BaseInitStructure.TIM_Prescaler =71;                       //預分頻71,即72分頻,得1M
    TIM_BaseInitStructure.TIM_ClockDivision = 0;
    TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1, &TIM_BaseInitStructure);
    //TIM1_OC1模塊設置
    TIM_OCStructInit(& TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode =TIM_OCMode_Toggle;             //管腳輸出模式:翻轉
    TIM_OCInitStructure.TIM_Pulse =2000;                           //翻轉周期:2000個脈沖
    TIM_OCInitStructure.TIM_OutputState =TIM_OutputState_Enable;   //使能TIM1_CH1通道
    TIM_OCInitStructure.TIM_OCPolarity =TIM_OCPolarity_High;       //輸出為正邏輯
    TIM_OC1Init(TIM1,&TIM_OCInitStructure);                        //寫入配置
    //清中斷
    TIM_ClearFlag(TIM1, TIM_FLAG_CC1);
    //TIM1中斷源設置,開啟相應通道的捕捉比較中斷
    TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
    //TIM1開啟
    TIM_Cmd(TIM1, ENABLE);
    //通道輸出使能
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
Step5.中斷服務子程序
void TIM1_CC_IRQHandler(void)
{
    u16 capture;
    if(TIM_GetITStatus(TIM1, TIM_IT_CC1) == SET)
    {
        TIM_ClearITPendingBit(TIM1, TIM_IT_CC1 );
        capture = TIM_GetCapture1(TIM1);
        TIM_SetCompare1(TIM1, capture + 2000);
        //這里解釋下:
        //將TIM1_CCR1的值增加2000,使得下一個TIM事件也需要2000個脈沖,
        //另一種方式是清零脈沖計數器
        //TIM_SetCounter(TIM2,0x0000);
    }
}
關于TIM的操作,要注意的是STM32處理器因為低功耗的需要,各模塊需要分別獨立開啟時鐘,所以,一定不要忘記給用到的模塊和管腳使能時鐘,因為這個原因,浪費了我好多時間阿~~!

九九的STM32筆記(二)TIM模塊產生PWM
這個是STM32的PWM輸出模式,STM32的TIM1模塊是增強型的定時器模塊,天生就是為電機控制而生,可以產生3組6路PWM,同時每組2路PWM為互補,并可以帶有死區,可以用來驅動H橋。
下面的代碼,是利用TIM1模塊的1、2通道產生一共4路PWM的代碼例子,類似代碼也可以參考ST的固件庫中相應example
C語言: TIM1模塊產生PWM,帶死區
   
//Step1.開啟TIM和相應端口時鐘
//啟動GPIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| \
                       RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\
                       ENABLE);
//啟動AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//啟動TIM1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
//Step2. GPIO做相應設置,為AF輸出
//PA.8/9口設置為TIM1的OC1輸出口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//PB.13/14口設置為TIM1_CH1N和TIM1_CH2N輸出口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//Step3. TIM模塊初始化
void TIM_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
    //TIM1基本計數器設置(設置PWM頻率)
    //頻率=TIM1_CLK/(ARR+1)
    TIM_BaseInitStructure.TIM_Period = 1000-1;
    TIM_BaseInitStructure.TIM_Prescaler = 72-1;
    TIM_BaseInitStructure.TIM_ClockDivision = 0;
    TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_BaseInitStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM1,&TIM_BaseInitStructure);
    //啟用ARR的影子寄存器(直到產生更新事件才更改設置)
    TIM_ARRPreloadConfig(TIM1, ENABLE);

    //TIM1_OC1模塊設置(設置1通道占空比)
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState =TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    TIM_OCInitStructure.TIM_Pulse = 120;
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);
    //啟用CCR1寄存器的影子寄存器(直到產生更新事件才更改設置)
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    //TIM2_OC2模塊設置(設置2通道占空比)
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState =TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 680;
    TIM_OC2Init(TIM1, &TIM_OCInitStructure);
    //啟用CCR2寄存器的影子寄存器(直到產生更新事件才更改設置)
    TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
  
    //死區設置
    TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
    TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
    TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
    TIM_BDTRInitStructure.TIM_DeadTime =0x90;   //這里調整死區大小0-0xff
    TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
    TIM_BDTRInitStructure.TIM_BreakPolarity =TIM_BreakPolarity_High;
    TIM_BDTRInitStructure.TIM_AutomaticOutput =TIM_AutomaticOutput_Enable;
    TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
  
    //TIM1開啟
    TIM_Cmd(TIM1, ENABLE);
    //TIM1_OC通道輸出PWM(一定要加)
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
}

其實,PWM模塊還可以有很多花樣可以玩,比方在異常時(如CPU時鐘有問題),可以緊急關閉輸出,以免發生電路燒毀等嚴重事故


《九九的STM32筆記》整理(2)


這是一個綜合的例子,演示了ADC模塊、DMA模塊和USART模塊的基本使用。
我們在這里設置ADC為連續轉換模式,常規轉換序列中有兩路轉換通道,分別是ADC_CH10(PC0)和ADC_CH16(片內溫度傳感器)。因為使用了自動多通道轉換,數據的取出工作最適合使用DMA方式取出,so,我們在內存里開辟了一個u16AD_Value[2]數組,并設置了相應的DMA模塊,使ADC在每個通道轉換結束后啟動DMA傳輸,其緩沖區數據量為2個HalfWord,使兩路通道的轉換結果自動的分別落到AD_Value[0]和AD_Value[1]中。
然后,在主函數里,就無需手動啟動AD轉換,等待轉換結束,再取結果了。我們可以在主函數里隨時取AD_Value中的數值,那里永遠都是最新的AD轉換結果。
如果我們定義一個更大的AD_Value數組,并調整DMA的傳輸數據量(BufferSize)可以實現AD結果的循環隊列存儲,從而可以進行各種數字濾波算法。
接著,取到轉換結果后,根據V=(AD_Value/4096)*Vref+的公式可以算出相應通道的電壓值,也可以根據 T(℃) =(1.43 - Vad)/34*10^(-6) + 25的算法,得到片內溫度傳感器的測量溫度值了。
通過重新定義putchar函數,及包含"stdio.h"頭文件,我們可以方便的使用標準C的庫函數printf(),實現串口通信。
相關的官方例程,可以參考FWLib V2.0的ADC\ADC1_DMA和USART\printf兩個目錄下的代碼。
本代碼例子是基于萬利199的開發板EK-STM32F實現,CPU=STM32F103VBT6


#i nclude "stm32f10x_lib.h"
#i nclude "stdio.h"


#defineADC1_DR_Address   ((u32)0x4001244C)


vu16 AD_Value[2];
vu16 i=0;
s16 Temp;
u16 Volt;

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART1_Configuration(void);
void ADC1_Configuration(void);
void DMA_Configuration(void);
int fputc(int ch, FILE *f);
void Delay(void);
u16 GetTemp(u16 advalue);
u16 GetVolt(u16 advalue);


int main(void)
{
   RCC_Configuration();
   GPIO_Configuration();
   NVIC_Configuration();
   USART1_Configuration();
   DMA_Configuration();
   ADC1_Configuration();
  
   //啟動第一次AD轉換
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
   //因為已經配置好了DMA,接下來AD自動連續轉換,結果自動保存在AD_Value處
  
    while(1)
    {
       Delay();
       Temp = GetTemp(AD_Value[1]);
       Volt = GetVolt(AD_Value[0]);
       USART_SendData(USART1,0x0c);      //清屏
       //注意,USART_SendData函數不檢查是否發送完成
       //等待發送完成
       while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
       printf("電壓:%d.%d\t溫度:%d.%d℃\r\n", \
           Volt/100, Volt0, Temp/100, Temp0);
      
    }
}

int fputc(int ch, FILE *f)
{
   //USART_SendData(USART1, (u8) ch);
   USART1->DR = (u8) ch;
  
   
   while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }
    returnch;
}

void Delay(void)
{
    u32 i;
   for(i=0;i<0x4f0000;i++);
   return;
}                          

u16 GetTemp(u16 advalue)
{
    u32Vtemp_sensor;
    s32Current_Temp;
  
//   ADC轉換結束以后,讀取ADC_DR寄存器中的結果,轉換溫度值計算公式如下:
//         V25 - VSENSE
// T(℃) = ------------ + 25
//          Avg_Slope
//   V25: 溫度傳感器在25℃時的輸出電壓,典型值1.43 V。
// VSENSE:溫度傳感器的當前輸出電壓,與ADC_DR寄存器中的結果ADC_ConvertedValue之間的轉換關系為:
//           ADC_ConvertedValue * Vdd
// VSENSE = --------------------------
//           Vdd_convert_value(0xFFF)
// Avg_Slope:溫度傳感器輸出電壓和溫度的關聯參數,典型值4.3 mV/℃。
   Vtemp_sensor = advalue * 330 / 4096;
    Current_Temp= (s32)(143 - Vtemp_sensor)*10000/43 + 2500;
    return(s16)Current_Temp;
}


u16 GetVolt(u16 advalue)
{
    return(u16)(advalue * 330 / 4096);
}
                        

void RCC_Configuration(void)
{
    ErrorStatusHSEStartUpStatus;
   //使能外部晶振
   RCC_HSEConfig(RCC_HSE_ON);
   //等待外部晶振穩定
   HSEStartUpStatus = RCC_WaitForHSEStartUp();
   //如果外部晶振啟動成功,則進行下一步操作
   if(HSEStartUpStatus==SUCCESS)
    {
       //設置HCLK(AHB時鐘)=SYSCLK
       RCC_HCLKConfig(RCC_SYSCLK_Div1);
       //PCLK1(APB1) = HCLK/2
       RCC_PCLK1Config(RCC_HCLK_Div2);
       //PCLK2(APB2) = HCLK
       RCC_PCLK2Config(RCC_HCLK_Div1);
      
       //設置ADC時鐘頻率
       RCC_ADCCLKConfig(RCC_PCLK2_Div2);
       //FLASH時序控制
       //推薦值:SYSCLK = 0~24MHz  Latency=0
       //       SYSCLK = 24~48MHz Latency=1
       //       SYSCLK = 48~72MHz Latency=2
       FLASH_SetLatency(FLASH_Latency_2);
       //開啟FLASH預取指功能
       FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
       //PLL設置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
       RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
       //啟動PLL
       RCC_PLLCmd(ENABLE);
       //等待PLL穩定
       while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
       //系統時鐘SYSCLK來自PLL輸出
       RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
       //切換時鐘后等待系統時鐘穩定
       while(RCC_GetSYSCLKSource()!=0x08);
      
    }
   //下面是給各模塊開啟時鐘
   //啟動GPIO
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB| \
                          RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\
                          ENABLE);
   //啟動AFIO
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
   //啟動USART1
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
   //啟動DMA時鐘
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
   //啟動ADC1時鐘
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
}


void GPIO_Configuration(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   //PC口4567腳設置GPIO輸出,推挽 2M
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
   GPIO_Init(GPIOC, &GPIO_InitStructure);
    //KEY2KEY3 JOYKEY
    //位于PD口的3 411-15腳,使能設置為輸入
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11| GPIO_Pin_12 |\
       GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
   GPIO_Init(GPIOD, &GPIO_InitStructure);
   //USART1_TX
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
  
   //USART1_RX
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
   GPIO_Init(GPIOA, &GPIO_InitStructure);
  
   //ADC_CH10--> PC0
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
   GPIO_Init(GPIOC, &GPIO_InitStructure);
}

void NVIC_Configuration(void)
{
   NVIC_InitTypeDef NVIC_InitStructure;
#ifdef VECT_TAB_RAM
    // Set theVector Table base location at 0x20000000
   NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else
    // Set theVector Table base location at 0x08000000
   NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
   //設置NVIC優先級分組為Group2:0-3搶占式優先級,0-3的響應式優先級
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
   //串口中斷打開
   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init(&NVIC_InitStructure);
}


void USART1_Configuration(void)
{
   USART_InitTypeDef USART_InitStructure;
  
   USART_InitStructure.USART_BaudRate = 19200;
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;
   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_Tx |USART_Mode_Rx;
   USART_Init(USART1, &USART_InitStructure);
  
   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  
   USART_Cmd(USART1, ENABLE);
}

void ADC1_Configuration(void)
{
   ADC_InitTypeDef ADC_InitStructure;
   ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
   ADC_InitStructure.ADC_ScanConvMode = ENABLE;
   ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //連續轉換開啟
   ADC_InitStructure.ADC_ExternalTrigConv =ADC_ExternalTrigConv_None;
   ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
   ADC_InitStructure.ADC_NbrOfChannel =2;    //設置轉換序列長度為2
   ADC_Init(ADC1, &ADC_InitStructure);
  
   //ADC內置溫度傳感器使能(要使用片內溫度傳感器,切忌要開啟它)
   ADC_TempSensorVrefintCmd(ENABLE);
  
   //常規轉換序列1:通道10
   ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1,ADC_SampleTime_13Cycles5);
   //常規轉換序列2:通道16(內部溫度傳感器),采樣時間>2.2us,(239cycles)
   ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2,ADC_SampleTime_239Cycles5);
  
    // EnableADC1
   ADC_Cmd(ADC1, ENABLE);
    //開啟ADC的DMA支持(要實現DMA功能,還需獨立配置DMA通道等參數)
   ADC_DMACmd(ADC1, ENABLE);
  
    //下面是ADC自動校準,開機后需執行一次,保證精度
    // EnableADC1 reset calibaration register
   ADC_ResetCalibration(ADC1);
    // Check theend of ADC1 reset calibration register
   while(ADC_GetResetCalibrationStatus(ADC1));
    // StartADC1 calibaration
   ADC_StartCalibration(ADC1);
    // Check theend of ADC1 calibration
   while(ADC_GetCalibrationStatus(ADC1));
    //ADC自動校準結束---------------
  
}

void DMA_Configuration(void)
{
   DMA_InitTypeDef DMA_InitStructure;
  
   DMA_DeInit(DMA1_Channel1);
   DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
   DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&AD_Value;
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   //BufferSize=2,因為ADC轉換序列有2個通道
   //如此設置,使序列1結果放在AD_Value[0],序列2結果放在AD_Value[1]
   DMA_InitStructure.DMA_BufferSize = 2;
   DMA_InitStructure.DMA_PeripheralInc =DMA_PeripheralInc_Disable;
   DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
   DMA_InitStructure.DMA_PeripheralDataSize =DMA_PeripheralDataSize_HalfWord;
   DMA_InitStructure.DMA_MemoryDataSize =DMA_MemoryDataSize_HalfWord;
   //循環模式開啟,Buffer寫滿后,自動回到初始地址開始傳輸
   DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
   DMA_InitStructure.DMA_Priority = DMA_Priority_High;
   DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA1_Channel1, &DMA_InitStructure);
   //配置完成后,啟動DMA通道
   DMA_Cmd(DMA1_Channel1, ENABLE);
}


《九九的STM32筆記》整理
這次是RTC的筆記:)
RTC這東西暈暈的,因為一個模塊涉及到了RTC,BKP,RCC多個模塊,之間的關系讓人有點模糊
入門的知識請大家看手冊,我來總結:
總之,RTC只是個能靠電池維持運行的32位定時器over!
所以,使用時要注意以下問題:
1. 上電后要檢查備份電池有沒有斷過電。如何檢查? 恩,RTC的示例代碼中已經明示:
  往備份域寄存器中寫一個特殊的字符,備份域寄存器是和RTC一起在斷電下能保存數據的。
  上電后檢查下這個特殊字符是否還存在,如果存在,ok,RTC的數據應該也沒丟,不需要重新配置它
  如果那個特殊字符丟了,那RTC的定時器數據一定也丟了,那我們要重新來配置RTC了
  這個過程包括時鐘使能、RTC時鐘源切換、設置分頻系數等等,這個可以參考FWLib\example\RTC\Calendar的代碼
  在我的這個實例里,檢查備份域掉電在Init.c的RTC_Conig()中,函數內若檢測到BKP掉電,則會調用RTC_Configuration()2. 因為RTC的一些設置是保存在后備域中的,so,操作RTC的設置寄存器前,要打開后備域模塊中的寫保護功能。
3. RTC設定值寫入前后都要檢查命令有沒有完成,調用RTC_WaitForLastTask();

具體的RTC初始化代碼如下:
////////////////////////////////////////////////////////////////////////////////
// RTC時鐘初始化!
////////////////////////////////////////////////////////////////////////////////

void RTC_Configuration(void)
{
   //啟用PWR和BKP的時鐘(from APB1)
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
   //后備域解鎖
   PWR_BackupAccessCmd(ENABLE);
   //備份寄存器模塊復位
   BKP_DeInit();
   //外部32.768K其喲偶那個
   RCC_LSEConfig(RCC_LSE_ON);
    //等待穩定
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
   //RTC時鐘源配置成LSE(外部32.768K)
   RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
   //RTC開啟
   RCC_RTCCLKCmd(ENABLE);
   //開啟后需要等待APB1時鐘與RTC時鐘同步,才能讀寫寄存器
   RTC_WaitForSynchro();
   //讀寫寄存器前,要確定上一個操作已經結束
   RTC_WaitForLastTask();
   //設置RTC分頻器,使RTC時鐘為1Hz
    //RTC period= RTCCLK/RTC_PR = (32.768 KHz)/(32767+1)
   RTC_SetPrescaler(32767);

   //等待寄存器寫入完成
   RTC_WaitForLastTask();
   //使能秒中斷
   RTC_ITConfig(RTC_IT_SEC,ENABLE);  
   //等待寫入完成
   RTC_WaitForLastTask();
   return;
}

void RTC_Config(void)
{
   //我們在BKP的后備寄存器1中,存了一個特殊字符0xA5A5
   //第一次上電或后備電源掉電后,該寄存器數據丟失,
   //表明RTC數據丟失,需要重新配置
    if(BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)
    {
       //重新配置RTC
       RTC_Configuration();
       //配置完成后,向后備寄存器中寫特殊字符0xA5A5
       BKP_WriteBackupRegister(BKP_DR1, 0xA5A5);
    }
    else
    {
       //若后備寄存器沒有掉電,則無需重新配置RTC
       //這里我們可以利用RCC_GetFlagStatus()函數查看本次復位類型
       if (RCC_GetFlagStatus(RCC_FLAG_PORRST) != RESET)
       {
           //這是上電復位
       }
       else if (RCC_GetFlagStatus(RCC_FLAG_PINRST) != RESET)
       {
           //這是外部RST管腳復位
       }
       //清除RCC中復位標志
       RCC_ClearFlag();
       //雖然RTC模塊不需要重新配置,且掉電后依靠后備電池依然運行
       //但是每次上電后,還是要使能RTCCLK???????
       //RCC_RTCCLKCmd(ENABLE);
       //等待RTC時鐘與APB1時鐘同步
       //RTC_WaitForSynchro();
       //使能秒中斷
       RTC_ITConfig(RTC_IT_SEC, ENABLE);
       //等待操作完成
       RTC_WaitForLastTask();
    }
#ifdef RTCClockOutput_Enable
   
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP,ENABLE);
   
   PWR_BackupAccessCmd(ENABLE);
   
   BKP_TamperPinCmd(DISABLE);
   
   BKP_RTCOutputConfig(BKP_RTCOutputSource_CalibClock);
#endif
   return;
}


《九九的STM32筆記》整理3

基于STM32處理器
RTC只是個能靠電池維持運行的32位定時器over!
并不像實時時鐘芯片,讀出來就是年月日。。。
看過些網上的代碼,有利用秒中斷,在內存中維持一個年月日的日歷。
我覺得,這種方法有很多缺點:
1.斷電時沒有中斷可用
2.頻繁進中斷,消耗資源
3.時間運算復雜,代碼需要自己寫
4.不與國際接軌。。。。
so,還是用標準的UNIX時間戳來進行時間的操作吧!
什么是UNIX時間戳?
UNIX時間戳,是unix下的計時方式。。。很廢話
具體點:他是一個32位的整形數(剛好和STM32的RTC寄存器一樣大),表示從UNIX元年(格林尼治時間1970-1-10:0:0)開始到某時刻所經歷的秒數
聽起來很玄幻的,計算下:32位的數從0-0xFFFFFFFF秒,大概到2038年unix時間戳將會溢出!這就是Y2038bug
不過,事實上的標準,我們還是照這個用吧,還有二十年呢。。。
UNIX時間戳:1229544206 <==>現實時間:2008-12-17 20:03:26
我們要做的,就是把當前時間的UNIX時間戳放在RTC計數器中讓他每秒++,over
然后,設計一套接口函數,實現UNIX時間戳與年月日的日歷時間格式轉換 這樣就可以了
在RTC中實現這個時間算法,有如下好處:
1. 系統無需用中斷和程序來維持時鐘,斷電后只要RTC在走即可
2. 具體的兩種計時的換算、星期數計算,有ANSI-C的標準C庫函數實現,具體可以看time.h
3. 時間與時間的計算,用UNIX時間戳運算,就變成了兩個32bit數的加減法
4. 與國際接軌。。。

幸好是與國際接軌,我們有time.h幫忙,在MDK的ARM編輯器下有,IAR下也有
其中已經定義了兩種數據類型:unix時間戳和日歷型時間
time_t:      UNIX時間戳(從1970-1-1起到某時間經過的秒數)
    typedef unsigned int time_t;

struct tm:   Calendar格式(年月日形式)
同時有相關操作函數
gmtime,localtime,ctime,mktime等等,方便的實現各種時間類型的轉換和計算
于是,基于這個time.h,折騰了一天,搞出了這個STM32下的RTC_Time使用的時間庫

這是我的RTC_Time.c中的說明:
本文件實現基于RTC的日期功能,提供年月日的讀寫。(基于ANSI-C的time.h)

作者:jjldc (九九)
QQ: 77058617

RTC中保存的時間格式,是UNIX時間戳格式的。即一個32bit的time_t變量(實為u32)
ANSI-C的標準庫中,提供了兩種表示時間的數據 型:
time_t:      UNIX時間戳(從1970-1-1起到某時間經過的秒數)
    typedef unsigned int time_t;

struct tm:   Calendar格式(年月日形式)
   tm結構如下:
   struct tm {
      int tm_sec;   // 秒 seconds afterthe minute, 0 to 60
                       (0 - 60 allows for the occasional leap second)
      int tm_min;   // 分 minutes afterthe hour, 0 to 59
       int tm_hour; // 時 hours since midnight, 0 to 23
       int tm_mday; // 日 day of the month, 1 to 31
       int tm_mon;   // 月 months sinceJanuary, 0 to 11
       int tm_year; // 年 years since 1900
       int tm_wday; // 星期 days since Sunday, 0 to 6
       int tm_yday; // 從元旦起的天數 days since January 1, 0 to 365
        int tm_isdst; // 夏令時??Daylight Savings Time flag
        ...
    }
    其中wday,yday可以自動產生,軟件直接讀取
    mon的取值為0-11
   ***注意***:
   tm_year:在time.h庫中定義為1900年起的年份,即2008年應表示為2008-1900=108
    這種表示方法對用戶來說不是十分友好,與現實有較大差異。
    所以在本文件中,屏蔽了這種差異。
    即外部調用本文件的函數時,tm結構體類型的日期,tm_year即為2008
    注意:若要調用系統庫time.c中的函數,需要自行將tm_year-=1900

成員函數說明:
struct tm Time_ConvUnixToCalendar(time_t t);
    輸入一個Unix時間戳(time_t),返回Calendar格式日期
time_t Time_ConvCalendarToUnix(struct tm t);
    輸入一個Calendar格式日期,返回Unix時間戳(time_t)
time_t Time_GetUnixTime(void);
    從RTC取當前時間的Unix時間戳值
struct tm Time_GetCalendarTime(void);
    從RTC取當前時間的日歷時間
void Time_SetUnixTime(time_t);
    輸入UNIX時間戳格式時間,設置為當前RTC時間
void Time_SetCalendarTime(struct tm t);
    輸入Calendar格式時間,設置為當前RTC時間

外部調用實例:
定義一個Calendar格式的日期變量:
struct tm now;
now.tm_year = 2008;
now.tm_mon =11;       //12月
now.tm_mday = 20;
now.tm_hour = 20;
now.tm_min = 12;
now.tm_sec = 30;

獲取當前日期時間:
tm_now = Time_GetCalendarTime();
然后可以直接讀tm_now.tm_wday獲取星期數

設置時間:
Step1. tm_now.xxx = xxxxxxxxx;
Step2. Time_SetCalendarTime(tm_now);

計算兩個時間的差
struct tm t1,t2;
t1_t = Time_ConvCalendarToUnix(t1);
t2_t = Time_ConvCalendarToUnix(t2);
dt = t1_t - t2_t;
dt就是兩個時間差的秒數
dt_tm =mktime(dt);   //注意dt的年份匹配,ansi庫中函數為相對年份,注意超限
另可以參考相關資料,調用ansi-c庫的格式化輸出等功能,ctime,strftime等


回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 精品国产一区二区三区久久狼黑人 | av在线播放国产 | 在线a视频网站 | 日韩在线小视频 | 午夜欧美 | 国产精品久久久久久久久久免费看 | 一级毛片免费看 | 99精品电影 | www国产精品| 欧美网站一区二区 | 欧美日韩一区二区在线 | 久久国产精品-国产精品 | 黄色小视频入口 | 欧洲国产精品视频 | 91精产国品一二三区 | 日韩a在线| 欧美一级在线免费观看 | 亚洲视频二区 | 搞黄网站在线观看 | 夜夜草| 亚洲 一区 | 日韩在线观看视频一区 | 欧美在线一区二区三区 | 日本中文在线 | 日本在线看 | 国内精品在线视频 | 久久国内精品 | 每日更新av | 91精品国产手机 | 99精品视频在线观看免费播放 | 在线中文视频 | gogo肉体亚洲高清在线视 | 免费天天干 | 在线观看国产网站 | 狠狠骚 | 亚洲日本成人 | 国产精品久久精品 | 欧美一a| 久久久成人精品 | 欧美一区二区三区在线看 | 亚洲精品国产偷自在线观看 |