'아두이노'에 해당되는 글 5건

  1. 2018.10.25 아두이노로 ATTiny85의 타이어1 PWM 사용하기.
  2. 2014.09.27 아두이노 전원 문제.
  3. 2014.09.22 아두이노를 이용한 원격 리모트 컨트롤.
  4. 2014.08.09 Code Block for arduino.
  5. 2014.08.08 아두이노를 이용한 쿨링팬 컨트롤.
2018.10.25 10:18

아두이노로 ATTiny85의 타이어1 PWM 사용하기.

 아두이노가 세상에 나온건 참 고마운 일이다. AVR 칩들의 프로그래밍을 더 쉽게 만들었다.

 돌아다니던 중에  8핀의 ATTiny85가 PWM 기능을 가진 것을 확인하고 그 기능이 정말 궁금했다.

 많은 인터넷 문서들을 읽은 후에야 그 기능을 제대로 사용할 수 있었다.


 일단 아두이노에서 ATTiny85를 사용하기 위해서는  ATTinycore라는 애드온을 넣어야 한다.

 그리고 소스 파일에 헤더를 넣고 평상시의 프로그래밍 그대로 코딩을 하면 된다.


 주의할 점은 핀 번호가 기존의 아두이노의 정의를 사용할 수 없다. 그리고 ADC 같은 경우에는 특별한 명칭의 A로 시작하는 번호를 analogRead에 사용해야 정상적으로 읽어올 수 있다.

 그리고 내부 헤더 소스파일의 정의를 보면 ATTiny85가 millis()나 delay()에 사용하는 타이머는 0번이다.


 동작 클럭은 1MHz로 하는게 좋은데, 만약 8MHz로 하려면 lfuse를 62 -> e2로 변경해야 한다. 개인적으로 쓰는 openprog가 fuse 셋팅을 지원하기는 하는데 이 div8을 fuse로 사용하면 인식이 안된다. 먹통이 되어버리는데 연속적으로 fuse를 62로 쓰기를 계속하면 다시 사용가능한 상태로 돌려진다.

 오래된 openprog 0.9.0 펌웨어와 0.9.1 컨트롤 프로그램으로는 HV 시리얼 프로그래밍이 지원되기는 하나 칩리스트 목록의 ATTiny85 설정으로 인식하지 못하는 바람에 ATTiny88로 설정해야 프로그램이 가능하다.


 ATTiny85는 타이머1은 특이하게 PLL 클럭을 소스로 PWM을 활성할 수 있다. 이 기능은 정말 놀라운게 128KHz(256KHz도 가능)의 PWM 클럭을 만들 수 있다. 컴퍼레이터의 약간의 설정 버그도 있는데 그 설정을 다 포함한 간단한 사용 코드는 아래와 같다.


  #define PWM PIN_B1


  pinMode(PWM, OUTPUT);

  // Timer1 PWM, 128KHz
  PLLCSR |= (1<<PLLE);
  while ((PLLCSR & (1<<PLOCK)) == 0x00)
    {
        // Do nothing until plock bit is set
    }
  PLLCSR |= (1<<PCKE);
  TCCR1 = (1<<CTC1)    |  // Enable PWM
          (1<<PWM1A)   |  // Set source to pck
          (1<<(CS11))  |  // PCK/2
          (1<<COM1A1);    // Clear the OC1A output line.
  GTCCR |= (1<<COM1B1);  // fix bug
  //TIMSK = (1<<OCIE1A) | (1<<TOIE1);
  OCR1A = 0;
  OCR1C = 255;


  OCR1A = 126;


 이는 6번 핀을 이용해서 PWM 클럭을 내보내는 코드로 다른 핀도 가능하다. 총 4개의 PWM이 가능하지만 3개만이 제대로 사용할 수 있다. 4개의 PWM을 모두 사용하면 6번핀은 타이머 오버플로우나 컴퍼레이터 인터럽트에서 핀의 신호를 처리해주어야 하는 번거로움이 있다. 하지만 예제와 같은 1개만이나 타이어0과 겹치지 않게 PWM을 사용하면 문제가 없다.


 여튼 대충 이렇게 PWM을 만들 수 있다.


 (추가) ATTinycore의 도움말에 설명되어 있기는 하지만, 이 타이머1의 PLL 클럭소스 방법은 2.7v 이하의 동작전압에서는 불안정하다. 그런 이유로 BOD를 2.7v로 설정하는게 좋을 수 있다.





Trackback 0 Comment 0
2014.09.27 16:40

아두이노 전원 문제.

 아두이노로 원격리모트 컨트롤을 만들려니 몇가지 문제에 봉착.


>> 과연 꺼진 것은 어떻게 확인할 것인가?

 이는 단순하게 전원을 제어할 수 있는 릴레이를 추가. 전원 컨트롤 기능도 넣었다. 하지만 또다른 문제가 생겨났으니.


>> 220VAC 5A의 12V구동 텍셀 릴레이의 전원부를 구성과 외부구성 사용으로 인한 아두이노 전원부의 큰 발열. 그냥 연결만 해도 열이 폴폴. 12릴레이는 Vin을 사용하기에 문제가 없지만, 아두이노 자체의 5V와 3.3V의 전원은 문제가 심각했다. 이는 어쩔 수 없이 2개의 전압을 출력하는 새로운 전원 공급기를 제작했다. 그렇게 문제는 해결되는 듯 싶었으나, 말도 못하게 심한 발열이 나는 전원 공급기. 달랑 7805와 3055트랜지스터를 붙였는데, 7805는 열도 안나는데 3055는 엄청난 발열이 난다. 결국 어디선가 입수한 손바닥 만한(약간 적은) 방열판을 부착. 일반 방열판은 안될 것 같다. 그렇게 USB단자로 전원을 공급하기로 하니 아두이노 전원부의 발열 문제는 해결. 3.3V 레귤레이터는 깜빡 확인을 안했다. 제어칩에서는 열이 폴폴 나던데 =_=;


 이렇게 두가지 문제의 해결을 보고 끝 =ㅅ=; 대충 전원 컨트롤이 이상없이 제어되는 것은 확인.



Average | 1/10sec | 0.00 EV | 3.7mm | ISO-800 | Off Compulsory


'하드웨어' 카테고리의 다른 글

avr-gcc 4.9.2  (0) 2014.10.14
USBTinyISP 만들기.  (0) 2014.10.09
아두이노 전원 문제.  (0) 2014.09.27
간단한 순차점등 LED회로.  (0) 2014.09.22
아두이노를 이용한 원격 리모트 컨트롤.  (0) 2014.09.22
5V 고전류 출력 전원회로.  (0) 2014.08.30
Trackback 0 Comment 0
2014.09.22 13:22

아두이노를 이용한 원격 리모트 컨트롤.

 갑작스레 원격 컨트롤 이야기가 나와서 만들게 된 원격 리모트컨트롤 모듈. 이더넷 보드인 enc28j60을 결합하고 적외선 LED를 3번에 연결하면 끝난다. 이더넷 모듈은 13번부터 8번까지를 사용하는데, 9번과 10번은 연결하지 않는다. 하드웨어는 비교적 간단하고 중요한 부분은 프로그래밍 부분이었다.


 일단 라이브러리를 구해야 하는데, 

https://github.com/shirriff/Arduino-IRremote

https://github.com/jcw/ethercard

에서 받을 수 있다.


주의할 점은 enc28j60은 전원이 3.3v라서 5v를 연결하면 큰일난다. 신호입력은 큰 문제가 없는 것 같고, 칩이 구동시에 약간 따끈한 정도로 아마 발열이 조금 있는 듯 싶다.


 IR Remote모듈의 수신은 이더넷 모듈과 같이 사용할 수 없다. 아마도 인터럽트쪽을 사용하는 것 같은데 그로인해서 기능상의 문제가 일어나는 듯 싶다.


 몇몇 스크래치 예제들을 통해서 코드를 구성하면 다음과 같다. 



/* ethercard and irDA remote control */

#include <EtherCard.h>
#include <IRremote.h>

//#define IR_ONLY
#ifndef IR_ONLY
// ethernet interface mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x35 };
// ethernet interface ip address
static byte myip[] = { 192,168,1,200 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };

byte Ethernet::buffer[700];
static long timer;
BufferFiller bfill;
byte irdata[104];
#endif

bool success_dhcp=false;

IRsend irsend;
#define IR_NORECV
#ifndef IR_NORECV
int recv_pin=5;
IRrecv irrecv(recv_pin);
decode_results results;
#endif
long lg_on=0x20df10ef;

char* hextobyte(char *str,byte *ret)
{
  *ret=0;
  byte cnt=0;
  while((str!=NULL)&&(*str!=0)&&(*str>32)) {
    if((*str>='0')&&(*str<='9')) {
      *ret+=*str-'0';
    } else
    if((*str>='a')&&(*str<='f')) {
      *ret+=10+(*str-'a');
    } else
    if((*str>='A')&&(*str<='F')) {
      *ret+=10+(*str-'A');
    } else {
      return NULL;
    }
    str++;
    cnt++;
    if(cnt==2) break;
    if((*str!=0)&&(*str>32)) *ret<<=4;
  }
  return str;
}

void ethernet_setip() {
    #ifndef IR_ONLY
    if (ether.begin(sizeof Ethernet::buffer, mymac) == 0)
    Serial.println( "Failed to access Ethernet controller");
  
    #define STATIC_IP
    #ifndef STATIC_IP
    success_dhcp=ether.dhcpSetup();
    #endif
    if(success_dhcp) {
      ether.printIp("My IP: ", ether.myip);
      // ether.printIp("Netmask: ", ether.mymask);
      ether.printIp("GW IP: ", ether.gwip);
      ether.printIp("DNS IP: ", ether.dnsip);     
      Serial.println("success dhcp");
    } else {
       // set ip address 192.168.1.200
       ether.staticSetup(myip,gwip);
       success_dhcp=true;
       Serial.println("static ip address : 192.168.1.200");
    }
    #endif 
}

void setup () {
  Serial.begin(9600);
  #ifndef IR_NORECV
  irrecv.enableIRIn(); 
  #endif
 
  ethernet_setip();
  //irsend.sendNEC(lg_on,32);
}

const char http_OK[] PROGMEM =
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n\r\n";

const char http_Found[] PROGMEM =
    "HTTP/1.0 302 Found\r\n"
    "Location: /\r\n\r\n";

const char http_Unauthorized[] PROGMEM =
    "HTTP/1.0 401 Unauthorized\r\n"
    "Content-Type: text/html\r\n\r\n"
    "<h1>401 Unauthorized</h1>";

void loop() {
   // working
    #ifndef IR_NORECV
    if(irrecv.decode(&results)) {
      if(results.decode_type==LG) Serial.println("LG");
      Serial.println(results.value,HEX);
      irrecv.resume();
    }
    #else
    word len=ether.packetReceive();
    word pos=ether.packetLoop(len);
    char* wpos;
    if(pos) {
      bfill=ether.tcpOffset();
      char *data=(char*)Ethernet::buffer+pos;
      Serial.println(data);
      if(strncmp("GET /",data,5)!=0) {
        bfill.emit_p(http_Unauthorized);       
      } else {
        data+=5;
        if(data[0]==' ') {
          bfill.emit_p(http_OK);
          bfill.emit_p(PSTR("Arduino IRRemote server"));
        } else
        if(NULL!=(wpos=strstr(data,"q?data="))) {         
          Serial.println("start parsing");
          //Serial.println(wpos);
          byte byte2;
          wpos+=7;
          wpos=hextobyte(wpos,&byte2); // type
          irdata[0]=byte2;
          wpos=hextobyte(wpos,&byte2);  // bits
          irdata[1]=byte2;
          wpos=hextobyte(wpos,&byte2);  // repeat
          irdata[2]=byte2;
          wpos=hextobyte(wpos,&byte2);  // hz lo
          irdata[3]=byte2;
          wpos=hextobyte(wpos,&byte2);  // hz hi
          irdata[4]=byte2;        
          byte dpos=5;
          while((wpos!=NULL)&&(*wpos!=0)&&(*wpos>32)) {
            wpos=hextobyte(wpos,&byte2);
            irdata[dpos]=byte2;
            dpos++;
            if(dpos>=100) break;
          }
          irdata[dpos]=0;
          Serial.println("finish parsing");
          // send data if valid.
          char codetype[11];
          if(dpos>5) {
            switch(irdata[0]) {
              case 1: irsend.sendNEC(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"NEC",10); break;
              case 2: irsend.sendSony(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"Sony",10); break;
              case 3: irsend.sendRC5(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"RC5",10); break;
              case 4: irsend.sendRC6(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"RC6",10); break;
              case 5: irsend.sendDISH(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"DISH",10); break;
              case 6: irsend.sendSharp(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"Sharp",10); break;
              case 7: irsend.sendPanasonic(*((int *)&irdata[3]),*((long*)&irdata[5])); strncpy(codetype,"Panasonic",10); break; // word address, long data
              case 8: irsend.sendJVC(*((long *)&irdata[5]),irdata[1],irdata[2]); strncpy(codetype,"JVC",10); break;          // long data, repeat
              case 9: irsend.sendSAMSUNG(*((long *)&irdata[5]),irdata[1]); strncpy(codetype,"SAMSUNG",10); break;
              default: irsend.sendRaw((unsigned int*)&irdata[5],irdata[2],*(unsigned int*)&irdata[3]); strncpy(codetype,"Raw",10); break;   // word* data, len, hz | hz, data1, data2, ... | len = repeat
            }
          }
          bfill.emit_p(http_OK);
          char temp[10];
          bfill.emit_p(PSTR("\nIRRemote parsed : $D / type:$S, bits:$D, repeat(len):$D, address(hz):$D, long code:$S\n"), dpos, codetype, irdata[1], irdata[2], *(unsigned int*)&irdata[3],ultoa(*((long *)&irdata[5]),temp,16));
          // dump
          for(int i=5; i<dpos; i++) {
            bfill.emit_p(PSTR("$H"),irdata[i]);
          }
        } else {
          bfill.emit_p(http_Unauthorized);
        }
      }
      ether.httpServerReply(bfill.position());
    }
    #endif
}



 고정적 IP와 dhcp를 사용할 수 있는데, 일단은 고정적인 IP로 했다. dhcp도 무리없이 작동했지만 자꾸 변하면 기기의 주소를 찾기를 힘들다 =ㅅ=; LG는 sendNEC를 사용하면 정상적으로 작동하는 것을 확인했다. 다른 기기는 아직 모르겠다. 문제는 기기들의 코드를 모른다는 것. 그래서 데이터를 몽땅 다 보내는 방식으로 처리. 브라우져를 이용하면 전송한 데이터를 받아볼 수 있다. GET방식으로 보내는데 앞의 5바이트까지의 16진수는 특정한 값을 가지며 첫번째는 기기의 종류를 선택할 수 있다.


 아직 더 테스트가 필요하지만 웹서버 기능이 완전하게 작동하는 것으로 일단 만족.


Trackback 0 Comment 0
2014.08.09 13:23

Code Block for arduino.

 아두이노의 기본툴은 자바 기반이라 답답하고 또 답답한 경우가 많은데, code block for arduino는 이런 문제를 해결해준다.




 새로운 프로젝트를 열면 다음과 같은 화면에서 아두이노 프로젝트를 만들 수 있다. 작업을 위한 COM포트를 설정할 수도 있고 등등의 작업이 가능하고 가장 중요한 것은 자동완성이 지원된다는 것. 3글자 정도를 입력하면 자동완성이 뜨고 길게 입력하지 않고 입력이 가능하다.


 소스는 기본 아두이노툴과 완전히 같은데 다른 점 하나는 헤더가 윗줄에 표시된다. 이 헤더를 제거하면 아두이노의 정식툴과 같은 소스가 된다. 플래시 업로더가 약간 다른 것 같은데 약간의 차이가 있는 것 같은데 확실히는 모르겠다.


 아쉬운 점은 이 툴은 윈도우즈 전용이고 메뉴들이 한글이 아닌 영문으로 몽땅 표시된다는 것. 하지만 코드 편집에서는 기본툴보다는 낫다.


 오픈소스이며,

http://sourceforge.net/projects/arduinodev/files/?source=navbar 에서 다운로드가 가능. 35메가 정도의 용량을 가진다. 컴파일러가 포함되어 있어서 용량이 좀 크다.

 

 아누이노의 컴파일러가 자바 정도라고 생각하고 있었는데, 그게 아니고 그냥 C++이란걸 다시금 알았다. 문법이 조금 달라도 언어는 같으니.


 프로젝트는 폴더단위로 만들어지며, 기본 템플릿 소스가 만들어진다.


Trackback 0 Comment 0
2014.08.08 23:50

아두이노를 이용한 쿨링팬 컨트롤.

 간단하게 아두이노를 이용한 쿨링팬 컨트롤. 센서로는 NTC-502F397을 사용한다고 가정하고, 써미스터는 접지쪽에 연결하고 저항 2.2k를 VCC쪽에 연결하는 방식을 택했다.

 저항을 계산해내는 방식에 전압에 의지하는 공식은 오차를 동반하는 것 같아 저항을 기준으로 하는 공식으로 변경했다. 이 공식을 사용하면 전압의 변화와 상관없이 저항을 제대로 산출할 수 있다. 저항은 1%오차의 푸르딩딩한 저항을 사용하면 좋다.


/*
  Fan PWM Control by Temperature for arduino uno.

*/

int PinFanPWM=9;
int Pinanalog=0;

int Temp_low=20,Temp_high=30;         // min, max

long x=0, Resister_temp=2200;         // 2.2k upper resister with thermistor
long th=0;
float ce=0;

int FanPWMBase=77; // 30% in 255;
int FanPWMInc=(255-FanPWMBase)/(Temp_high-Temp_low)+1;  // (255-77)/10
int TempVal;
int FanPWM;

void setup()
{
    pinMode(PinFanPWM,OUTPUT);
    Serial.begin(9600);
}

void loop()
{
    // get temperature from your sensor
    TempVal=analogRead(Pinanalog);
    th = Resister_temp * TempVal / (1024-TempVal);  // get resister value.
    // ntc-502F397 formula
    ce = ((log(4.0*th - 3000.0) / (-0.024119329) + 473)-32.0)/1.8;

    // calc PWM Value
    TempVal=(int)ce;
    Serial.println(TempVal);
    if (TempVal<Temp_low) {
        FanPWM=FanPWMBase;
    } else {
        FanPWM=FanPWMBase+(TempVal-Temp_low)*FanPWMInc;
        if (FanPWM>255) {
            FanPWM=255;
        }
    }
    analogWrite(PinFanPWM,FanPWM);
    //Serial.println(FanPWM);
}


 아직 실험해보지 않은 코드로 PWM값과 온도값이 시리얼포트로 전송된다. 이것은 실험후 지워질 예정.


 20도에서 30도의 영역으로 20도에서는 30%PWM으로 30도에서는 풀로드가 된다. 트랜지스터 등을 이용해서 12V를 공급하는 방식으로 하면 된다.


 코드에 실수가 있어서 약간의 변경. ce를 TempVal로 변환하도록 조정.


 테스트를 해본 결과. 아두이노가 온도가 더 정확. 브레드보드의 접점의 금속탓인가. 여튼 더 정확해보이는 값이 나오는 것 같다. 아날로그 온도계만 있어서 정확한 온도는 모르겠다. PIC의 테이블 연산치보다는 정확한 듯 싶다.


 PWM도 정상적으로 값이 뜨는 것을 확인.


 아두이노의 온도관련 실험은 이걸루 끝 =_=;




Trackback 0 Comment 0