본문 바로가기
Programming/Embedded

아두이노 듀에의 주파수 출력 실험

by No Brainer 2022. 2. 19.

아두이노 듀에는 아두이노 사에서 제공하는 여타 아두이노시리즈 중에서 월등한 사양을 자랑합니다.

기본적으로 AVR 코어를 실장한 ATmega시리즈가 8비트 프로세서인 반면 듀에는 ARM 32비트 프로세서를 이용하며 

수많은 peripheral기능을 제공합니다. 여타 자세한 사항은  듀에의 사양과 관련해 포스팅한 글을 참조하면 됩니다.

 

해서 듀에의 출력 주파수를 어느정도 올려볼 수 있는지 간단한 실험을 해보았습니다.

 

1. 아두이노에서 제공하는 라이브러리를 사용

 

void setup() {
  pinMode(8, OUTPUT);
}
 
void loop() { 
  while (true) {
    digitalWrite(8, HIGH);
    digitalWrite(8, LOW);
  }
}
 
 
 
 


 
위의 코드를 듀에에 올려 출력한 파형으로, 240khz정도가 나옵니다.
우노로 실험했을때(아래사진)과 크게 성능차이를 볼 수 없습니다.
 



<우노로 실험한 내역>
 
 
2. 레지스터에 직접 접근하여 사용
 
void setup() { pinMode(8, OUTPUT); } void loop() { while (true) { g_APinDescription[8].pPort -> PIO_SODR = g_APinDescription[8].ulPin; g_APinDescription[8].pPort -> PIO_CODR = g_APinDescription[8].ulPin; } }
 

출력주파수가 대략 17MHZ정도로 월등히 속도가 빨라집니다.
 
 
 
3. 아두이노 듀 보드에서 제공하는 PWM peripheral 을 레지스터로 제어하여 pwm control
 
 
float duty=0; 
 
void setup()
{  
  Serial.begin(115200);
 
  PIOC->PIO_PDR = PIO_PC2 | PIO_PC3 | PIO_PC4 | PIO_PC5 | PIO_PC6 | PIO_PC7;//pc2~pc7의 pio(gpio사용기능해제)
 
 
  PIOC->PIO_ABSR |= PIO_PC2 | PIO_PC3 | PIO_PC4 | PIO_PC5 | PIO_PC6 | PIO_PC7 ;//PC2~7을 peripheral B로 설정
 
 
  pmc_enable_periph_clk(ID_PWM);//prtipheral pwm 에 공급되는 clock을 enable시킨다
 
  PWM-> PWM_DIS = (1u<<0) | (1u<<1) | (1u<<2) ;//pwm channel 0,1,2번을 disable시킨다. 
 
 
  PWM->PWM_CLK &= ~0x0F000F00; //PREB,PREA = 0000 -> MCK(84MHz)를 사용하겠다
  PWM->PWM_CLK &= ~0x00FF0000; //DIVB = 0x00 clkb turn off
  PWM->PWM_CLK &= ~0x000000FF; //DIVA = 0x00 clka역시 turn off
  PWM->PWM_CLK |= (0x000000FF & 1);  //DIVA = 1   DIVA=1
 
 
 
  PWM-> PWM_CH_NUM[0].PWM_CMR &= ~0x0000000F; //CPRE를 모두 0으로 CLEAR
  PWM-> PWM_CH_NUM[0].PWM_CMR |= 0xB; //CPRE=0XB=0b1011로 설정, 즉 clka를 선택함.
  PWM-> PWM_CH_NUM[0].PWM_CMR &= 1u<<8; //CALG=1, center aligned
  PWM-> PWM_CH_NUM[0].PWM_CMR &= ~(1u<<9); //CPOL=0
 
 
  
  PWM-> PWM_CH_NUM[0].PWM_CMR |= (1u<<16); //channel mode resistor deadtime generator enable
  PWM-> PWM_CH_NUM[1].PWM_CMR |= (1u<<16);
  PWM-> PWM_CH_NUM[2].PWM_CMR |= (1u<<16);
  
 
  PWM-> PWM_CH_NUM[0].PWM_DT = 0x00030003;//CHO: DTL=3, DTH=3
  PWM-> PWM_CH_NUM[1].PWM_DT = 0x00050005;//CH1: DTL=5. DTH=5
  PWM-> PWM_CH_NUM[2].PWM_DT = 0x000F000F;//CH2: DTL=15 DTH=15
 
 
 
 
  PWM-> PWM_CH_NUM[0].PWM_CPRD = 5; 
 
 
  PWM-> PWM_CH_NUM[0].PWM_CDTY = 0;
  PWM-> PWM_CH_NUM[1].PWM_CDTY = 0;
  PWM-> PWM_CH_NUM[2].PWM_CDTY = 0;
 
  PWM-> PWM_SCM |= 0x00000007; //same Source Sync pwm sync channels mode registor
  PWM-> PWM_SCM &= ~0x00030000;
 
 
  PWM->PWM_ENA=1u<<0; //0번 채널을 enable한 것.
 
 
}
 
void loop()
{
 
  //sim_time += 0.005;   //클수록 빨라짐 simulation time 갱신
  //duty = (uint32_t)(1050*cos(sim_time)+1050); //0<=듀티<=2100
 
  duty=4;
 
 
 
  PWM-> PWM_CH_NUM[0].PWM_CDTYUPD = duty;//PWM Channel Duty Cycle Update Register
  PWM-> PWM_CH_NUM[1].PWM_CDTYUPD = duty;//CDTYUPD는 This register acts as a double buffer for the CDTY value.
  PWM-> PWM_CH_NUM[2].PWM_CDTYUPD = duty;
 
  PWM->PWM_SCUC =1;//pwm 과 관련한 모든 정보를 갱신하게 해주는 레지스터이다. 항상 1로 해놓자.
  
  Serial.println(duty);
  delay(1);
}
 



해당 코드를 사용하여, 약 17MHZ까지는 올려보았습니다.
하지만 configure을 어설프게 한 탓인지 그 이상으로 올리진 못했습니다.
 
이후 해외유저들이 작성한 코드를 발견하게 되었는데 , one bit PWM을 이용하여 매우 높은 주파수를 출력할 수 있었습니다.
master clock인 84mhz뿐만 아니라 그 이상의 주파수도 출력할 수 있는것으로 보입니다


uint32_t pwmPin = 8;
uint32_t maxDutyCount = 2;
uint32_t clkAFreq = 42000000ul;
uint32_t pwmFreq = 42000000ul; 
void setup()
 {
  pmc_enable_periph_clk(PWM_INTERFACE_ID);
  PWMC_ConfigureClocks(clkAFreq, 0, VARIANT_MCK);
  PIO_Configure(
    g_APinDescription[pwmPin].pPort,
    g_APinDescription[pwmPin].ulPinType,
    g_APinDescription[pwmPin].ulPin,
    g_APinDescription[pwmPin].ulPinConfiguration);
  uint32_t channel = g_APinDescription[pwmPin].ulPWMChannel;
  PWMC_ConfigureChannel(PWM_INTERFACE, channel , pwmFreq, 0, 0);
  PWMC_SetPeriod(PWM_INTERFACE, channel, maxDutyCount);
  PWMC_EnableChannel(PWM_INTERFACE, channel);
  PWMC_SetDutyCycle(PWM_INTERFACE, channel, 1);
  pmc_mck_set_prescaler(2);
}
void loop() 
{
}