2016-01-20 64 views
-1

我很熟悉ATmega128的PWM生成和微控制器系列任意PWM信號。我一直在使用預標量和其他寄存器來產生頻率。但是我必須產生20KHz的PWM信號。我試過,但我無法得到所需的輸出。任何人都可以建議我或幫助我如何做到這一點?生成使用ATMEGA128

據我所知,在ATmega128的,1個指令需要1個週期。使用16MHz晶振,1條指令在1/16M秒內完成。 我試圖用25us的佔空比產生20Khz的信號(50us)。但我在示波器中獲得的頻率遠遠小於20KHz(277.78 Hz) 我的計算是 16MH = 20000Hz * 800. 爲0-399計數,我使端口爲高電平,並且 爲399-799計數,我使端口爲低電平。

void frequency(void){ // 20kHz Frequency 
    if (cnt1 <= 399){ 
     PORTB |= (1<<7); 
    } else { 
     PORTB &= ~(1<<7); 
    } 
    cnt1++; 
    if (cnt1 >= 800) cnt1 = 0; 
} 
+0

當我用Xmega解決這個問題時,我驚訝於爲​​了獲得穩定的計算頻率而採取了多少步驟。 (例如,請參閱http://craigbot.blogspot.it/2014/01/stepper-gnomebot.html)。大多數情況下,我只是憑經驗使用高頻單循環和循環內的循環對於較低頻率,將迭代器計數與示波器結果相匹配。它是線性的,所以做起來相當簡單。 – Dribbler

+0

你看過你的程序集輸出,並檢查了編譯器__really__向你扔了多少條指令?或者,你可能想考慮基於定時器的中斷來控制時序。 – MikeD

回答

1

我沒有訪問128但驗證其16位定時器1是類似於在328和32U4,所以下面應該工作與微小的修改(主要關鍵點可能是查找什麼引腳寄存器溢出必將):

#include <avr/io.h> 
#include <util/delay.h> 

struct CTC1 
{ 
    static void setup() 
    { 
     // CTC mode with TOP-OCR1A 

     TCCR1A = 0; 
     TCCR1B = _BV(WGM12); 

     // toggle channel A on compare match 

     TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0); 

     // set channel A bound pin PB1 to output mode 

#if defined(__AVR_ATmega32U4__) 
     DDRB |= _BV(5); 
#else 
     DDRB |= _BV(1); 
#endif 
    } 

    static void set_freq(float f) 
    { 
     static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256); 

     uint16_t n; 

     if (f >= f1)  n = 1; 
     else if (f >= f8) n = 8; 
     else if (f >= f64) n = 64; 
     else if (f >= f256) n = 256; 
     else    n = 1024; 

     prescale(n); 

     OCR1A = static_cast<uint16_t>(round(F_CPU/(2 * n * f) - 1)); 
    } 

    static void prescale(uint16_t n) 
    { 
     uint8_t bits = 0; 

     switch (n) 
     { 
      case 1: bits = _BV(CS10);    break; 
      case 8: bits = _BV(CS11);    break; 
      case 64: bits = _BV(CS11) | _BV(CS10); break; 
      case 256: bits = _BV(CS12);    break; 
      case 1024: bits = _BV(CS12) | _BV(CS10); break; 
      default: bits = 0; 
     } 

     TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits; 
    } 

    static inline float min_freq(uint16_t n) 
    { 
     return ceil(F_CPU/(2 * n * 65536)); 
    } 
}; 

void setup() 
{ 
    CTC1::setup(); 
    CTC1::set_freq(20e3); 
} 

void loop() 
{ 
    // do whatever 
    _delay_ms(1); 
} 

int main() 
{ 
    setup(); 
    for (;;) 
     loop(); 
} 

我在我的測試範圍,準確地測量爲20kHz掀起了328P在16MHz的運行。如果20kHz是您需要的唯一頻率,那麼您可以大幅度簡化。翻譯代碼以使用8位定時器之一也很簡單,雖然我還沒有證實可以用這些定時器準確地達到20kHz。

0

這不是一個好主意,在C中使用計數器來實現PWM或任何時間關鍵。儘管C將您的代碼轉換爲特定的機器代碼,但您並不知道需要多少時間。 您的代碼不會翻譯爲:

make port B high 400 times (PORTB |= (1<<7);) 
make port B low 400 times (PORTB &= ~(1<<7);) 

,而是像這樣(簡化,人類可讀):

load variable cnt1 to memA; 
load 399 to memB 
compare mem A to memB 
put result to memC 
if memC eq "somthing indicating <=" do PORTB |= (1<<7); 
if memC something else do PORTB &= ~(1<<7); 
load cnt1 to memD and increment; 
write memD to cnt1; 
load 800 to memE 
load cnt1 to memF 
compare memF to memE 
put result to memG 
if memG eq "somthing indicating <=" do memF = 0, write memF to cnt1; 
if memG something else go to start; 

如果你看看這個但從「C」點你需要做的至少

1. comare cnt1-399 
2. if ok - do/else 
3. port high/port low 
4. add one to cnt1 
5. compare cnt1 and 800 

那就要看你編譯器如何去它在優化所有負載和寫入(通常相當好)。 如果你真的瞭解你的編譯器,並且不使用太多的優化(通常很複雜),或者用匯編編寫代碼,你可以控制延遲是什麼。但是,那麼您將不得不使用類似於我對機器代碼解釋的邏輯(彙編器接近人類可讀的機器代碼)。

我認爲解決的辦法因爲你是計時器中斷。對於atmega128這個here有一個很好的教程。

而且你是什麼意思有:

我試圖生成25US佔空比20KHZ信號(50美元)。

你的意思是20kHz信號佔空比爲50%嗎?所以25us低,25 us高?

如果是這種情況,你可以用一個定時器中斷和一個(二進制)計數器來實現。 您可以在提供的鏈接中閱讀有關「8位計時器示例」的內容。