Search

2013年2月6日 星期三

PCM2706 Receive

    最近想自己DIY個輕便只用USB供電的DAC玩玩,把PCM2706當Receive然後走I2S到PCM1794接著在給管機放大機或直接上耳機喇叭...等等,本來一直在想要不要把PCM2706和PCM1794作在同一塊PCB,不過想了想還是先作分開比較方便測試,昨天先完成Receive的部份了,做了測試一開始很容易斷斷續續重新被OS掛載,然後我把PCM2706的腳重新焊了一次就比較沒有這個問題了,但是我注意到跟之前做的PCM2706的問題一樣,有時後會使Debian(其他Linux不知道,Win7不會)操作定住.....所以可能還得測試幾天看情況如何,如果有人真的做了有問題可以回報給我感恩XD,年後再來畫PCM1794。

說明:


    板子都是單面板且零件都是DIP,有主電路板與一個PCM2706的DIP轉板,PCM2706焊在DIP轉板時那個點要對準轉板的正方形點這樣方向才正確,然後插到主電路也是正方形點對正方形點,JP1是用來控制Pin9,如果Pin9接地則使用I2S輸出,否則就是一般輸出,我是只要用I2S不過為了直接測試我還是畫上一個左右聲道輸出的線路(下圖青色線以上的電路),因此功能開關也省去了,然後參考Datasheet與別人的圖以後用USB供電要將D+加上一個pull up電阻連接HOST,而振盪器也是根據建議用12MHz並一個1M電阻與兩個27p陶瓷電容。


Part List:

  1. USB:USB B-TYPE
  2. TX1:12MHz振盪器
  3. JP1:3Pin
  4. IS2:6Pin
  5. 左右聲耳機座
  6. L1:3~5mm LED
  7. R1、R2、R3、R4:33 Ohm
  8. R5:1M Ohm(振盪器下方)
  9. R6:330 Ohm
  10. R7、R8:22 Ohm
  11. R9、R10:1.5k Ohm
  12. R11、R12:15 Ohm
  13. R13、R14、R15、R16:3.3k Ohm
  14. C1、C2:27pF陶瓷電容
  15. C3、C4、C5、C6:1uF/16v電解、鉭質電容
  16. C7、C8:47uF/25v電解電容
  17. C9、C10:100uF/25v電解電容
  18. C11、C12:22nF金屬皮膜電容
    那條青色線以上是一般輸出,我是拿來測試用,如果用不到只要用I2S可以把那條線以上的電路直接刪去(用剪刀沿著那條線減去),另外注意PCM2706左上有三顆很靠近的電容中間那顆是C7旁邊兩顆是C3、C4,而C7、C8要躺著否則可能會卡到PCM2706的板子。


完成(只留I2S就這樣大小)


完成(有左右聲道)




2013年1月29日 星期二

Electronic Candle

  剛剛看我妹清出一個電子蠟燭XD,用一個電容式麥克風和一個RGB色LED以及一個被封住不知名的MCU(比ATtiny85還小的感覺),不過可惜的是麥克風只有開關的功能,如果顏色會隨麥克風受力大小改變會有真實蠟燭的感覺XD(這個顏色變化是固定隨機XD)。





2013年1月28日 星期一

wxArduino IDE(?) Compile and Uploader Test

  今天早上沒事做想說來寫個Arduino IDE順便研究AVR-Toolchain的Command,UI用wxWidgets刻然後底下包AVR-GCC相關工具 and Avrdude,前者要負責編譯code以及用到的Arduino Library,所以自己的IDE要寫個解析Include多少Arduino Library的Function,最後將這些編譯後檔案的obj File與Arduino自帶的Core328(ATmega328) Link完之後產生HEX File,再由給定的Command(MCU的Baudrate と Clock と Serial Port..etc)傳給Avrdude然後將HEX File上傳到MCU上,用wxWidgets可以偷懶直接用wxExecute之類的在背景開個Shell去跑AVR-Toolchain,不過我比較喜歡用該平台的API另外開個Thread去跑,下面一個簡單的demo,主要把Blink Examples Code編譯然後上傳到Arduino,第二次把Code暗與亮從50ms改成500ms然後重新編譯上傳看到LED差異證明確實有功能。




參考:


  1. Arduino Build Process
  2. Arduino Uploader – command line utility for compiling and uploading Arduino sketch
  3. WinAVR 初體驗

2013年1月24日 星期四

關於Servo選擇

選擇力矩的大小 = 重心重量 X 重心到Servo距離 X Sin(角度)

假設有一機器人上半身約重0.7/kg,而上半身到腳踝關節的距離是10/cm,當腳踝彎曲45度時,則: 0.7/kg * 10/cm * 0.707 = 4.949 kg/cm

PID Controller

  去年暑假無意間看到有paper用基因演算法去自動調整PID控制參數,感覺上非常實用也很有趣不過工作很忙一直沒時間研究,這幾天又想趁有空閒來實現那個paper,又突然看到Arduino PID Library的作者的Blog解說,發現跟一般普通理想化的PID公式不同,因此讀完了以下參考前八篇(Arduino PID Library)以及第九篇(補充說明),跟著每一篇實作code與推導公式收益良多!

PID.h


#ifndef __PID__
#define __PID__

const bool DIRECT = true;
const bool REVERSE = false;

const bool ON = true;
const bool OFF = false;

class PID
{
   public:
      PID(double*,double*,double*,double,double,double,unsigned long,bool,double,double,double);
      void Computing();
      void SetTunings(double,double,double);
      void SetSampleTime(unsigned long);
      void SetOutputLimits(double,double);
      void SetOnOff(bool);
      void SetDirection(bool);

   private:
      double Kp,Ki,Kd;
      double *input,*output;
      double *setpoint;

      double integral;
      double last_input;

      double min,max;

      unsigned long last_time;
      unsigned long sample_time;

      bool on_off;
      bool direction;
};

#endif


PID.cpp


#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include "PID.h"

PID::PID(double *_input,double *_output,double *_setpoint,
      double _Kp,double _Ki,double _Kd,
      unsigned long _sample_time = 100000,
      bool _on_off = OFF,double _direction = DIRECT,
      double _min = 0,double _max = 255)
{
   input = _input;
   output = _output;
   setpoint = _setpoint;

   sample_time = _sample_time;

   on_off = _on_off;

   SetOutputLimits(_min,_max);

   SetDirection(_direction);

   SetTunings(_Kp,_Ki,_Kd);

   last_time = (micros() - sample_time);
}

void PID::Computing()
{
   if(on_off == OFF){
      return ;
   }

   unsigned long now = micros();

   if((now - last_time) >= sample_time){
      double error = (*setpoint - *input);

      integral += (Ki * error);
      if(integral > max){
integral = max;
      }
      else if(integral < min){
integral = min;
      }

      double diff_input = (*input - last_input);

      *output = (Kp * error) + integral - (Kd * diff_input);

      if(*output > max){
*output = max;
      }
      else if(*output < min){
*output = min;
      }

      last_input = *input;
      last_time = now;
   }
}

void PID::SetTunings(double _Kp,double _Ki,double _Kd)
{
   if((_Kp < 0) || (_Ki < 0) || (_Kd < 0)){
      return ;
   }

   double SampleTimeInSec = ((double)sample_time) / 1000000.0f;

   Kp = _Kp;
   Ki = _Ki * SampleTimeInSec;
   Kd = _Kd / SampleTimeInSec;

   if(direction != REVERSE){
      Kp = (0 - Kp);
      Ki = (0 - Ki);
      Kd = (0 - Kd);
   }
}

void PID::SetSampleTime(unsigned long _sample_time)
{
   if(_sample_time > 0){
      double ratio = ((double)_sample_time / (double)sample_time);

      Ki *= ratio;
      Kd /= ratio;

      sample_time = _sample_time;
   }
}

void PID::SetOutputLimits(double _min,double _max)
{
   if(_min > _max){
      return ;
   }

   min = _min;
   max = _max;

   if(on_off == ON){
      if(*output > max){
*output = max;
      }
      else if(*output < min){
*output = min;
      }

      if(integral > max){
integral = max;
      }
      else if(integral < min){
integral = min;
      }
   }
}

void PID::SetOnOff(bool _on_off)
{
   bool new_state = (_on_off == ON);

   if(new_state == (!on_off)){
      integral = *output;
      last_input = *input;

      if(integral > max){
integral = max;
      }
      else if(integral < min){
integral = min;
      }
   }

   on_off = new_state;
}

void PID::SetDirection(bool _direction)
{
  if((on_off == ON) && (_direction != direction)){
      Kp = (0 - Kp);
      Ki = (0 - Ki);
      Kd = (0 - Kd);
   }

   direction = _direction;
}




參考:

  第一篇是基本的PID公式,第二篇將採樣時間固定而消去幾次除法運算(Kalman Filter等等的微積分時間也可以這樣簡化計算),第三篇說明如何做Anti-Derivative Kick,第四篇說明如何消去中途修改PID參數的落差,第五篇說明如何做Anti-Reset Windup,第六篇其實就是說中途中止計算要記得直接跳出不要更改狀態,第七篇接續前一篇如果要恢復記得要回到上次計算的狀態,第八篇說明計算結果方向(正負號),例如Input大於Setpoint就增加Output(正向),或者Input小於Setpoint就增加Output(反向),第九篇算是補充說明積分項計算時間點的差異。

  1. Improving the Beginner’s PID – Introduction
  2. Improving the Beginner’s PID – Sample Time
  3. Improving the Beginner’s PID – Derivative Kick
  4. Improving the Beginner’s PID: Tuning Changes
  5. Improving the Beginner’s PID: Reset Windup
  6. Improving the Beginner’s PID: On/Off
  7. Improving the Beginner’s PID: Initialization
  8. Improving the Beginner’s PID: Direction
  9. PID: When Should I Compute the Integral Term?