2015-02-15

#if defined (ARDUINO) && ARDUINO >= 100 ? 어디서 비교하는지 궁금합니다.


아두이노 호환 구문 관련 질문을 정리한 글입니다.

출처 : cafe.naver.com/laydigital/

공개용 코드들을 참조하기 위해 살펴보다가 유독 아래와 같은 구문들이 자주 보여서 검색해보니 버젼 호환과 관련된 내용이라는 것을 알게 되었습니다.

http://forum.arduino.cc/index.php?topic=61764.5;wap2

if (ARDUINO <  100)
#include <WProgram.h>
#else
#include <Arduino.h>
#endif
------------------------------------------

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif


그런데 여기서 한가지 궁금한 점이 있어 질문드립니다.

ARDUINO100 이란 상수값과 비교한다는건 ARDUINO가 numeric value라는 얘기인데
이값이 정의된 곳이 어디인지 찾아보기가 힘들어서 질문드립니다 현재 ARDUINO는 사용하고 있지 않고 있어서
전체 lib파일들을 전부 흝어 볼수가 없고 지금 참고용으로 살펴보고 있는 코드와 관련된 공개용 소스만 보고 있다가
궁금해서 질문 드립니다.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

C:\Program Files\Arduino\hardware\tools\avr\bin\avr-gcc -c -g -Os -Wall -ffunction-sections -fdata-sections -mmcu=atmega328p -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=106 -IC

컴파일될때 -DARDUINO=106
이렇게 전달되네요.
IDE 내부에서 전달할 수도 있을듯 합니다.

16BIT-TIMMER 관련 질문

서보를 사용하면서 16비트 타이머 관련 질문을 정리한 내용입니다.

출처 : cafe.naver.com/laydigital/


안녕하세여, 오랜만에 질문을 드리네요.

현재 프로젝트로 6DOF - stewart platform을 만들고 있는데요, 16bit timer를 쓸려고 살펴보는 중에 다음과 같은

부분이 이해가 좀 않되서 질문 드립니다.



위의 그림에서 Duty를 주는 부분이 8bit 에서는 TOP값이 고정되어 OCRn이었는데 16bit에서는 WGMn3:0 값에 따라 달라진다고
나와 있는데요,




질문1)  TOP값이 FIXED일 경우, ICRnA의 경우 Duty값으로 비교되는 값이 아래 지문처럼 OCRnA reg를 자유롭게 사용하여 PWM파형을 출력하여 사용한다고 나와 있는데 OCRnA의 경우에 Duty값으로 비교되는 값이 어떤값으로 되는지 어느부분에 명시된건지 잘 파악되지 않아 질문드립니다.




질문2) Figure 52. FAST PWM모드에서 OCRnA이 non fixed TOP일경우 TOP value를 바꿔주므로써 기본 PWM주파수를 의도적으로 바꾸는데 좋은 장점이 있다는 부분이 위 지문에 언급되어 나오는데 이부분이 OCRnA값을 TOP으로 주고 duty값도 OCRnA값으로 비교한다는 말인지, 어떤지 실제로 코드로 구현하면 16bit 타이머1을 시작할때 한번 TOP값을 set 하고 이후에 다시 어떻게 TOP값을 주는다는건지 이해가 않되서 이부분도 질문드립니다.

servo 모터는 skyholic DGS-1188 http://www.servodatabase.com/servo/skyholic/dgs-1188 6개를 구매해서
 http://www.youtube.com/watch?v=ezp4ZvfJv0g 이 친구들처럼 제작해보고자 하는데 진행하면서 질문 좀 드리겠습니다 ㅎ; servo 모터는 8bit timer로 FAST PWM 모드로 61HZ 파형으로 duty를 변경해서 테스트 해본결과 작동은 하는데 50hz 안에서 20ms 주기로 fast pwm으로 6개의 OCn pin을 사용 출력으로 이용해보고자 16bit timer를 살펴보게 됐습니다.



/////////////////////////////////////////////////////////////////////////////////////////////////////////

1) 만약 제가 정상적으로 이해를 했다면
ICRn을 사용하는 경우는 이해를 하신거구
OCRnA를 TOP 값으로 사용한 경우의 비교 값을 말씀하시는 거죠?
OCRnA는 OCR1A,OCR3A 둘중 하나이고(타이머에 따라)
OCR1A 라면 정상적으로 만들 수 있는 PWM 신호는 두개(OCR1B,OCR1C에 의한) 이죠.

2) 
그냥 TOP 에 해당하는 레지스터는 동적으로 바꾸셔도 되는데, 다음의 경우는 조심해야 합니다.
내부 카운터가 이전 TOP을 지나가고 있을떄, 만약 해당 TOP값을 낮은 값으로 바꾸면, 이전에 진행하던
CNT 값이 이미 비교 레지스터를 지나버려서 MAX값까지 올라갔다 오는게 한번 생깁니다.
동작은 내부 TCNT가 있고, 어디까지(TOP)을 설정하는 레지스터(ICRn,OCRnA)와 그 pwm의 듀티
(OCRnA,OCRnB,OCRnC where ICRn을 TOP으로 한 경우, 
OCRnB,OCRnC where OCRnA가 TOP인 경우)


JKIT-128-1 : failed to enter programming mode!

Failed to enter programming mode!


출처 : cafe.naver.com/laydigital/

안녕하세요, 오늘 마지막 강좌 연습을 하던도중 다음과 같은 Error가 발생하여 문의 드립니당.
첫번째와 두번째 사진까지 정상 진입하였습니다.. 아무런 에러도 없었는데
두번째 사진에서 ISP 모드에서 memory -> program을 클릭 하면 아래 3번째 사진과 같이 에러 메세지와 함께 세번째 사진과
같은 팝업 안내창 출력됩니다. 지금까지 아무 문제 없다가 오늘 갑자기 그래서 COM3 ->5로 바꿔도 보고 재부팅도 해보고 재실행도 해봤지만 현상이 계속 지속되서 문의 드립니당.
제품은    jkit-128-1
프로그램 atmel studio 6.1




일단 기본적인 해결책은 모듈 및 키트에 연결된 외부 모듈 들을 전부 제거하여 avr-kit 와 컴퓨터 사이에 서로 연결이 원할한지 다시 체크 해보는게 첫째 입니다. 킷트에 문제가 없다면
현재 연결하려는 선의 port pin들이 ISP와 과련된 핀에 연결되는지 체크 해보시면 큰 무리없이 해결 될 것입니다. 이렇게 해도 상황이 개선 되지 않는다면 일단 프로그램을 재설치 해보시고 이후에도 상황이 변화가 없다면 chip에 문제가 있을 수 있으니 모듈 미치 킷트 제조사에 문의 해보시기를 추천합니다. 

Dot-matrix 연습 영상

전광판 출력 연습 강좌란에 나오는 내용을 연습한 코드 및  정리 자료 입니다.

출처 : cafe.naver.com/laydigital/

안녕하세요, 어느덧 강좌가 2개만 남았습니다.... 남은 강좌도 마무리까지 열심히 달려서 보충자료에 있는 과제와 연습 코드까지 다룰수 있도록 힘내봐야겠네요. youtube도 시작할겸 강좌 구동영상을 연습삼아 올려봅니다. 2 color dot matrix 품명은 BVD-8518SG1 이고 datasheet가 없다네요 ;; 하드웨어 연결은 강좌에 나온 사항을 토대로 연결하였습니당.



Code simulation video



// CODE

#include <avr/io.h>
#define Kilo 1000UL
#define Mega Kilo * Kilo
#define F_CPU 16*Mega
#include <util/delay.h>

void dsp(uint8_t *p,uint8_t);

uint8_t prr[]= {           
    0,0,0,0,0,0,0,
    0b00111100, //I
    0b00011000,
    0b00011000,
    0b00011000,
    0b00011000,
    0b00011000,
    0b00011000,
    0b00111100,
    0,0,
    0b01100110, //♥
    0b11111111,
    0b11111111,
    0b11111111,
    0b11111111,
    0b01111110,
    0b00111100,
    0b00011000,
    0,0,    
    0b01100110, //Y
    0b01100110,
    0b01100110,
    0b01111110,
    0b01111110,
    0b00011000,
    0b00011000,
    0b00011000,
    0,0,
    0b00111100, //O
    0b01111110,
    0b11100111,
    0b11000011,
    0b11000011,
    0b11100111,
    0b01111110,
    0b00111100,
    0,0,
    0b01100110, //U
    0b01100110,
    0b01100110,
    0b01100110,
    0b01100110,
    0b01100110,
    0b01111110,
    0b00111100,
    0,0,0,0,0,0,0,0
    };

int main(void)
{
 DDRA = 0xff;
 DDRE = 0xff; //red
 DDRF = 0xff; //green
 uint8_t i,j=0;

 while(1)
  {
       for(i=0; i<((sizeof(prr)/sizeof(prr[0]))-8); i++)
    {
     for(j=0; j<80; j++)
     {
       dsp(prr,i);  
     }
    }
  }
    
}

void dsp(uint8_t *p,uint8_t t)
{
  uint8_t i=0;
  for(i=0; i<8; i++)
  {
    PORTA = (1<<PA7)>>i;
    PORTF = *((p++)+t); //green
 PORTE = PORTF&0xf0; //red
 _delay_us(500);
  }

}








CLCD + AVR 을 이용하여 문자 쓰기.

CLCD를 사용하여 좌우명 새기기 라는 강좌를 진행하며 정리한 글입니다.

출처 : cafe.naver.com/laydigital/

안녕하세요 8.2 Clcd 강좌를 진행후 궁금증이 생겨서 질문드립니다.

강좌에서 Write timing diagram을 토대로 timing 에따라 코드를 작성하다 실제로 제가 주는 _delay(sec)시간이 과연
정확한 시간인가하고 의아해졌습니다.

실제로 init_clcd  에서는 그대로 _delay()라이브러리 함수의 기능을사용하여 값을 datasheet value 그대로 주었고,
_delay_ms(1.53) or _delay_ms(2), _delay_us(39).. 등등  Write timing diagram에서는  시간단위가 ns 인데 MPU: 16MHZ
1clock당 62.5ns 로 계산하면 기본적으로 1clock에서 ~? clock 까지 한코드를 실행하는데 드는 clock 만 가지고도 딜레이 없이 사용해도 괜찮은건지 궁금합니당. 제가 작성한 코드는 아래와 같이.. write_dsp_timing 에서 table 순서대로만 코드를 적고 마지막에 data를 입력 시키는 delay만 길게 잡고 실행해서 data를 화면에 표시를 했습니다. 마지막 delay 값을 ns 단위로 줬는데 data가 다 입력되지 않고 잘려서 들어갑니다.

여기서 궁금증을 정리하면..

질문1) 정확히 확인해보지는 못했지만 사실 방법도 아직 잘 모르지만 코드가 컴파일되면 1clock당 수행되는 코드형태로
바뀐다고 알고 있는데요 (assembly형태) 순서대로 딜레이 함수 추가 하지않고  기본적으로 한코드문 실행할경우
1clock~ 3clcok(16MHz = 62.5ns/1clock )으로 가정하고 순서대로 코드하여 사용해도 괜찮은건지 일단 궁금합니다.
여기서 좀 문제되는건 제가 작성한 코드가  각코드문에서 몇 clock씩 소요되는지 아직 확인 방법을 몰라 좀 애매합니다..

질문2) write_dsp_timing 마지막 delay값으로 그냥 적당한 값을 넣어주었는데요 (몇번 해보고 되는 값으로..) 실제로
delay 함수를 사용하여 write_dsp_timing 함수상에서 마지막으로 넣어줘야 하는 delay 값을 어림잡아서라도 계산값 으로 해보고
싶은데 영 어떻게 계산할지 방법이 않떠오릅니다 어떤식으로 계산해서 넣어주면 괜찮을지 힌트라도 주시면 감사하겠습니다.

질문3) datasheet 상에서 E Rise/Fall time  이값의 의미는 강좌에 설명되어져 있는데요, 실제로 이값은
어떤식으로 사용되는건지 궁금합니다. Max 20 (ns),  설명은 있는데 활용하는 경우가 없어서(?), 몰라서 이해하기 좀 난해합니다.
코딩할때나 하드웨어를 구성할때나 어떤경우에 이용되는건지 아니면 그냥 참고사항인지, 실제로 wiki나 각종블로그에는
화려한 공식들이 등장하는데요 이값이 어떤 쓰임이 있는지 알려주시면 감사하겠습니다.

-------------------------------------------------------------------------------------------------
아래는 연습용으로 첫번째 줄의 문자열이 한칸씩 >> 쉬프트하는 연습을 코드로 해보았습니다. 더 괜찮은 방법이 있다면 알려주시면 감사하겠습니다. 쉬프트하면 전에 있던 첫주소의 문자가 계속 중첩되서 다시 지우고 새로 쓰는 형태로 해보았습니다.
-----------------------------------------------------------------------------------------------------------------
#include <avr/io.h>
#define kilo 1000UL
#define Mega kilo*kilo
#define F_CPU 16*Mega // 1/16us  = 1clock=0.0625(us), 1clock=62.5(ns)
#include <util/delay.h>

#define Enable  PG4 //1: Enable , 0:disable
#define R_W     PG3 //1: Read, 0: Write , normal: 0 (write)
#define RS      PG2 //1: DR(Data reg), 0: IR(Instruction reg)
#define DB0     PA0 
#define DB1     PA1
#define DB2     PA2
#define DB3     PA3
#define DB4     PA4
#define DB5     PA5
#define DB6     PA6
#define DB7     PA7

#define IR 0
#define DR 1
#define READ 1
#define WRITE 0

void init_clcd(void);
void write_dsp_timing(int8_t,int8_t,uint8_t); //1cycle: 500ns = 8clock (upper)
void write_string(uint8_t *p);

uint8_t arr[]="Dyne Practice";
uint8_t brr[]="Good job !";
uint8_t crr[]={20,20,20,20,20,20,20,20,20,20,20,20};


int main(void)
{
  DDRA = 0xff;
  DDRG = 0xff;
     init_clcd();
     uint8_t i=0;
  //write_dsp_timing(DR,WRITE,'D');
   
 while(1)
 {       /*--set ADD---*/
         write_dsp_timing(IR,WRITE,(3<<DB6)); //2-line first address.
         write_string(brr);
    
     for(i=0; i<8; i++)
     {
      write_dsp_timing(IR,WRITE,((2<<DB6)+i)); //1-line first address.
      write_string(arr);
      /*--set ADD---*/
      _delay_ms(400);
      write_dsp_timing(IR,WRITE,((2<<DB6)+i)); //clear.
      write_string(crr); //disp clear.
   
     }
     
 }
 }

void init_clcd(void)
{

  /*----wait more 30ms,  minimum (30ms) -----*/
  _delay_ms(30);
  /*---funcition set  2-line mode(1), 5x7dot mode(0)----*/
  write_dsp_timing(IR,WRITE,(7<<DB3)); // PORTA = (7<<DB3);  
  /*----wait more 39us,  minimum (39us) -----*/
  _delay_us(39); 
  /*----Display on(1), curs off(0), Blink off(0)---*/
  write_dsp_timing(IR,WRITE,(3<<DB2));//PORTA = (3<<DB2);
  /*----wait more 39us,  minimum (39us) -----*/
  _delay_us(30);
  /*----Display clear(1)-----*/
  write_dsp_timing(IR,WRITE,(1<<DB0));//PORTA = (1<<DB0);
  /*----wait more 1.53ms,  minimum (1.53ms) -----*/
  _delay_ms(2);
  /*----Entry mode set -----*/
   write_dsp_timing(IR,WRITE,(3<<DB1));//PORTA = (3<<DB2);

}

void write_dsp_timing(int8_t rs, int8_t r_w, uint8_t Data)
{
 PORTG = (rs<<RS);         //IR
 PORTG |= (r_w<<R_W);    //Write
 PORTG |= (1<<Enable);   //1
 PORTA = Data;              //Data
 PORTG &= ~(1<<Enable);  //0
 _delay_ms(1);           //valid data timing
}

void write_string(uint8_t *p)
{
   while(*p)
   {
     write_dsp_timing(DR,WRITE,*p++);
   }
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




질문1) 정확히 확인해보지는 못했지만 사실 방법도 아직 잘 모르지만 코드가 컴파일되면 1clock당 수행되는 코드형태로 
바뀐다고 알고 있는데요 (assembly형태) 순서대로 딜레이 함수 추가 하지않고 기본적으로 한코드문 실행할경우 
1clock~ 3clcok(16MHz = 62.5ns/1clock )으로 가정하고 순서대로 코드하여 사용해도 괜찮은건지 일단 궁금합니다. 
그렇게 가정해서 하셔도 상관없습니다.
단 C로 만든 코드가 assembly 몇개 명령어로 만들어질지, 기계어를 보면서(lss화일) 확인 하시면 됩니다.

PORTA = 1;
PORTA = 0;
적어도 포트 A의 핀 0는 62.5 * 2 이상의 high pulse를 가집니다.(io register에 쓰기 assembly code
가 2 clock을 먹으므로)

타이밍상 100ns high 라면 위 두개의 assembly를 바로 사용해서도 delay가 만족합니다.

일반적으로는 IO핀의 변화에 대한 최소 시간이 있습니다.
특정포트의 핀을 0으로 만듬
특정포트의 핀을 1로 만듬
의 두 기계어가 있다고 하고 위 두 명령어 각각이 2 clock의 실행시간이 소요된다고 해서
실제로 IO핀이 해당 시간만큼만큼 따라주지 경우도 맣습니다.

(예를 들어 1Ghz cpu 가 bus 가 200Mhz 를 통해서 io 레지스터에 200MHz로 쓰기가 가능하다고 해서
io핀이 100Mhz(토글을 하므로 bus 속도의 반)이 되지 못합니다.
핀은 최대 움직일수 있는 속도가 있습니다.


만약 60ns 보다 작은 setup 타임이 필요한 타이밍이라면

PORTy = data; // 데이타를 싣고
PORTx = 0;
PORTx = 1; // 제어신호가 ->0->1 로 바뀌어야 하고 data의 setup 타임이 60ns 라면 
// 위 같이 delay 없이도 충분함... io register에 쓰기/읽기 최소 2clock이라 최소 130 ns 이상은 나옴.
와 같이 하셔도 충분히 0인 시간이 위 조건을 만족합니다.(따로 delay가 필요없음)

C 로 작성한 코드가 asembly로 생성되는 걸 보시고 필요한 경우
asm("nop");// 62.5 ns 추가
와 같은 assembly를 C 에 삽입하면 됩니다.

PORTG = (rs<<RS); //IR
PORTG |= (r_w<<R_W); //Write
PORTG |= (1<<Enable); //1
PORTA = Data; //Data
PORTG &= ~(1<<Enable); //0
_delay_ms(1); //valid data timing
[출처] 8.2 CLCD에 내 좌우명을 새겨라 [강좌]질문. (임베디드홀릭) |작성자 다인

위는
시작 
RS신호만 1로 만듬 
최소 62.5 * 2 이상 대기
R_W 신호 1로 만듬
최소 62.6 * 2 이상 대기
Enable 신호를 1로 만듬
최소 62.5 * 2 이상 대기
data를 씀
최소 62.5 * 2 이상 대기
Enable 신호 0으로 만듬
최소 62.5 * 2 이상 대기
---여기에서 더 필요한 시간은
Enable이 0이 된후 다시 위 과정이 반복되는걸 cycle time 이라고 하는데
위 시간을 다 합한것과 cycle time의 차(cycle 타임이 더 적으면 delay 필요없음,
cycle타임이 더 크면 해당 시간만큼 대기)

그런데 위에서
atmega128의 경우 PORTG는 io read write(비트 세트/clear) 명령어가 제공되지 않는 레지스터입니다.
즉 메모리 읽기/쓰기로 일어납니다.

PORTG |= 1 << Enable 
이건
reg = PORTG;
reg = reg | (1 << Enable);
PORTG = reg

이렇게 세 단계로 이루어집니다.
메모리에서 데티아 읽기, 쓰기, 그리고 레지스터에 OR 시키는 시간만큼 소요됩니다.
따라서 io bit set/clear 가 되는

PORTA |= 1 << Enable ;
가 sbis 같은 2cycle 명령어로 변환되는것도 다르게 시간이 훨씬 더 걸립니다.

기계어 생성된걸 봐야 하며 컴파일 옵션(옵티마이저)에 따라 다르므로
제가 위에서 최소라고 적은 겁니다. 실제 시간은 더 걸림.

강좌에 있는 CLCD 타이밍으로 본다면

RS와 R/W는 같은 타이밍에 쓸수 있음
E가 1로 바뀔때까지 RS, R/W는 Tsu1 시간이 필요하며, 예의 경우 40ns 이므로 delay 필요없음
E의 high 시간은 Tw이며 230 ns 이므로 명령어 실행시간 62.5 * 2 = 130과의 차 100ns 이므로 두개의 기계어
asm("nop"); // 62.5
asm("nop"); // 62.5
다시 E를 0으로 하면 130ns(최소 62.5 * 2) 인데 
새로 E를 high 할 수 있는 시간 Tc (cycle이 500 ns 이므로)

500 - (62.5 * 2 + 62.5 * 2(nop) + 62.5 * 2(0으로 쓰기)) =
500 - (130 + 130 + 130) = 500 - 390 = 110 

그런데 새로 시작할때, 다시 RS/RW를 쓰기 하고 다시 E를 1로 올리므로 이미 시간에 다 포함.
(또한 함수로 구현되어 있어서 함수 리턴 및 call 시간이 추가로 붙음)


Tr/Tf 은 길어야 20ns 입니다. 일반적으로 회로에서 mcu에 바로 clcd를 연결할 경우 mcu가 특정 핀을 쓰기 하면 신호가 바로 올라가므로 별로 신경 쓸 필요는 없는데,
혹시라도 해당 핀에 cap같은걸 달아서 천천히 올라가는 완만한 신호가 생성된다면 해당 규격에 어긋나죠
만약 clcd를 긴 케이블을 통해서 구동한다면 선이 길어지면서
저항과 cap 성분이 크져서 신호가 완만히 올라갈경우 위 시간이 meet하지 않는다면 다른 
방안(케이블을 좋은걸 사용하거나, 길이를 줄이거나) 을 강구해야 겠죠.

데이타쉬트 보시면
테이블 6에
bus timing (bus상에서 시간) 말고 명령어별로 delay시간이 우측에 있습니다.
즉 명령어에 따라 추가 시간이 필요합니다.

예를 들어 RS가 0인 것중에서도
clear display 명령어가 보낸후에는 최소 1.53 ms 까지 대기해야 하고
entry mode는 39 us 등

각 동작에 따른 delay 테이블을 두시거나,
해당 갑중 가능 큰값을 일괄 적용하시면 되겠죠.

bus 의 타이밍은 물리적은(전기적은) 규격이고, 그것을 통해 lcd에 명령이 전달되어서
해당 명령이 실행될때까지의 시간은 lcd 에서 내부적으로 더 필요한 시간입니다.

아 그래서 8-bit Initialization의 표와 같이 중간마다 명령어 수행후 최소 delay 값을 명시한거군요

그렇죠.
bus 타이밍은 전기적으로 상대편이 신호를 잘 받기 위한 규격이고
그렇게 전달된 명령/데이타가 내부에서 처리하는 시간이 있는거죠.

AVR과 대화하기 UART 연습강좌 과제

USART 강좌의 과제를 연습한 코드 및 답변내용을 정리한 글입니다.

출처 : cafe.naver.com/laydigital/

------------------------------------------------------------------------
[과제] 위의 UART 통신 예에서, BS(Back Space) 키를 누른 경우는 글자가 지워지지 않고 커서만 왼쪽으로 가게 되는데, 이것을 한 글자가 지워지면서 왼쪽으로 가도록 프로그램해 보세요.
------------------------------------------------------------------------
혹시 위에 과제가 stack ,array 나 queue 등을 이용한 echo input data를 관리하는 형태의 연습을 위한
과제가 아닌지 조심스럽게 여쭈어봅니다. 일단 저는 문자로 문자를 지우는 방법으로 쉽게 작성했습니다..;


#include <avr/io.h>
#define kilo 1000UL
#define Mega kilo*kilo
#define F_CPU 16*Mega
//#define FOSC 16000000 //clock speed
//#define BAUD 115200
//#define Myubrr FOSC/16/BAUD-1  // FOSC/16/BAUD -1 + (-35%)

#include <util/delay.h>

void USART0_SET(uint16_t);
void USART0_Transmit (uint8_t p);
void Transmit_string (uint8_t*p);
uint8_t USART0_Receive(void);


//uint8_t arr[] = {11,'D','Y','N','E',0x20,'C','O','M','P','A','N','Y','!',10,13};
uint8_t arr[] ="DynE Serial Practice>"; //uint8_t *arr="DynE Serial Practice>"; 
uint8_t prr[] = "I like you\r\n\n";  

int main(void)
{
 uint8_t cr; 
 USART0_SET(103);  //baud rate 9600

    while(1)
    {
           cr = USART0_Receive();
    USART0_Transmit(cr);
    
    switch(cr)
    {
      case  8: USART0_Transmit(255); USART0_Transmit(8); break;   //back space 
      case 27: USART0_Transmit(10);  USART0_Transmit(12); break;  //clear terminal
      case 13: USART0_Transmit(10);  Transmit_string(arr); break;    //enter
    }
   }
}

void USART0_SET(uint16_t ubrr)
{
  /*set baud rate */
 UBRR0H = (uint8_t)(ubrr>>8);
 UBRR0L = (uint8_t) ubrr;
 /* Enable receiver and transmitter */
 UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (0<<UCSZ02);  
   
 /*Set frame format : 8data, 1stop bit */
 UCSR0C = (0<<USBS0)|(3<<UCSZ00);
 /*Set Parity mode */
 UCSR0C |=(0<<UPM00); //disabled,  parity
}


void USART0_Transmit(uint8_t p)
{
  /* Wait for empty transmit buffer */
   while( !(UCSR0A &  (1<<UDRE0)) )
   ;
  /* Put data into buffer, sends the data */

   UDR0 = p;
   //_delay_ms(30); //A confirmed

}

void Transmit_string (uint8_t*p)
{
  while(*p)   USART0_Transmit(*p++);
}

uint8_t USART0_Receive(void)
{
 /* Wait for data to be received*/
 while( !(UCSR0A & (1<<RXC0)) )
 ;
 /* Get and return received data from buffer*/
 return UDR0;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


시리얼 모니터에서 enter 키를 0x0d('\r'), 0x0a('\n') 어떤 조합으로 보내는지에 따라 약간 수정을 해야 하지만

#define ENTER '\r'
#define BS '\b'
#define SPACE ' '

ch = getchar();
swtich(ch)
{
case ENTER : 할일을함/
break;
case BS : Tx(BS); Tx(SPACE); Tx(BS); break;
}
이런 식으로 합니다.

그런데 코드에서 255을 출력하는건 어떤 의미 인가요?

ASCII 확장코드 표에서 255 값이 blank 문자여서 backspace 와 조합하여 커서가 뒤로가면서 터미널에 표시된 문자를 지우는 역활로 이용했습니다. 문자를 빈칸문자로 다시 채우는 형태로 문자가 지워지게 보이게 하여 과제내용 구현 연습을 했습니다.

예 그렇게 하셔도 상관없겠네요.
보통 blank는 스페이스 문자 (' ' . 0x20) 으로 지우거든요..ㅎ

C언어 배열과 포인터 질문 정리

C언어를 배우면서 배열과 포인터에 대한 질문 내용을 정리한 글입니다.


출처 : cafe.naver.com/laydigital/


안녕하세요, 갑자기 궁금해서 질문을 쓰게 됐습니다.

2주정도 C언어 관련 문법들을 살펴보고 정리하다가, 이차원 배열 arr[][] 과 포인터에 관한 내용중 그 관계과 궁금하고 명확하게 설명한 자료가 없어 질문드립니다. 물론, 사용하는건 외워서라도 할수있지만 저의 경우 외워서 하는건 영 마음에 않들어서 이해를 통해 활용하는걸 가장 좋아하는 스타일이라 이렇게 질문 드려봅니다.

질문 내용은 다음과 같습니다.

제가 자료들을 토대로 정리하여 이해한 내용은  1차원 배열의 경우 int arr[]로 선언햇을경우 기본형은  arr= int(*)형 혹은 int* 형은 바로 int *ptr,  ptr =arr 이경우 ptr은 int * 형 , arr도 int(*),int* 형 그러므로 서로의 포인터형이 같은 형이므로 대응된다 여기까지는 참 이해하기 쉬운 부분이였습니다.

그런데 문제는 2차원 배열로 선언할 경우 입니다.

책, C/C++언어 관련사이트에선 다음과 같이 int arr[x][y] 일경우 int **ptr은 arr에 대응되지않고 다음과 같이 포인트배열을 선언
하여 그 주소값을 대응시켜 사용한다고 설명합니다.  int arr[2][3]; 일경우 대응되는 포인터는 int(*ptr)[3]입니다. 물론 그림과
주소 address 등을 첨부하여 잘 정리 되어있습니다 그런데 어디에도 (*prt)붙이는 이유에 대해서 설명이 없습니다.
여기서 어떤 블로그자료에선  int **ptr, prt= (int**)arr 이런식으로 형변환을 시켜서 거의 반 억지로 주소를 배분해서 쓰기도 하던데 저 경우 해본 결과 다음과 같이 할경우 어이없는 일이 벌어집니다.

int a=1 ; ptr = &a;   ptr = (int*)a; 이렇게 하면 주소를 변수a의 상수값 1을 가지고 *ptr 값을 출력해보면 unInitialized error가
발생합니다 당연히 address 1에는 어떤값도 정의 하거나 초기화하지 않았는데 저렇게 어거지로 집어넣으면 변수의 형이 맞기때문에 값은 들어갑니다.. 너무 억지스러워서 쓰기도 참 조심스러운 방법입니다..

제 경우 너무 궁금해서 일부러 visual studio에 컴파일 에러를 만들어서 현재 내가 전달할려는 변수의 수형이 어떤형인지를
일일 확인해 보았는데요.

1차원 배열의경우  int arr[],  arr = int(*), int*,  &arr=int(*)[]
2차원 배열의 경우 int arr[][], arr=int(*)[],    &arr=int(*)[][]

1차원 포인터배열  int(*prr)[], prr=int(*)[], &prr=int(**)[]
2차춴 포인터배열  int(*prr)[][], prr = int(*)[][], &prr=int(**)[][]

다음과 같이 형을 인식하여  prr = arr 은 서로 형이 일치하여 바로 대응 됩니다. 그래서 int arr[2][3], 은 int(*prr)[3]에 바로
대응하여 prr = arr; 이런식으로 바로 바로 대응하여 prr[1][1] = arr[1][1] 은 같은 값을 가리키게 됩니다.

여기까지는 다소의 수고가 들어도 확인 해보고 살펴볼수있는 충분한 방법은 많았습니다 하지만 정작 왜? 2차원 배열의포인터를 선언할 경우 (*ptr)안에 포인터를 선언하지는 어디에도 설명해주는 곳이 없습니다 제가 제일 궁금한건 (  ) 안에 왜 포인터를 선언해 주어야 하는것입니다, 어떤 수식, 형의 상관 관계 때문인지 ..이문제때문에 몇일을 고생했지만 마음에 드는 설명을 해주는곳이 없어서 그냥 묻어 두었다가 C언어 관련 질문은 어느때건 해도 된다고 하신 얘기가 기억나서 질문을 써봤습니다.

가장 근복적인 이해가 부족하면 활용하는데 큰 장애가 될 수있어서 다소 장황하지만 이렇게 질문드리오니 설명의 양이 많아
여건상 힘드시다면 관련 사이트를 알려주시면 참고하여 살펴보겠습니다. 사용법은 그냥 쓰면 되지만  분명 활용에 대한 제약은 굉장히 클거라고 느낍니다. 제경우 이쪽분야는 아니지만 제가 일한 분야는 기구, 금형, 자동차(Exterior), layout 설계인데요 제가 공부해온 경험으로 비추어보면 복잡한 논리와 이론도 가장 밑바탕에서 시작되는 충분한 이해를 기반으로 사용해야 아무리 어려워 보이는 설계나 기능 구현도 쉽게 할 수 있었거든요...  긴 질문 읽어주셔서 감사합니다 __)


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int arr[2][3];

arr[0]는 3개짜리 int 의 첫번째 주소(offset=0)
arr[1]은 3개짜리 int 의 두번쨰 주소(offset = 3 * 2) / int 가 2바이트 인경우
array선언시 arr는 컴파일시 해당 arry의 시작주소가 되는 상수입니다. 
변수로
arr[0]에 대응할려면
*p 가 되야죠. 
*p = p[] 와 같으므로
arr[]와 같은 레벨은 *p

int (*p)[3]; 이 int arr[2][3]의 속성과 같죠.

설명하기가 어렵네요.
복잡한 포인터 사용을 할떄는 저도 다시 찾아보고 사용합니다.
사용할 일이 별로 없어서....

왜 *p로 선언했는지를 궁금해 하신건가요?

선언은 p 라는 변수 하나(array 가 아닌) 선언하는 겁니다.

p 를 선언하는데
*p : p 가 가리키는 곳은
(*p)[3] : 3개짜리 array에 대한 주소이며
int (*p)[3]: 각 3개의 방은 int 타입이다.

()를 하지 않으면 우선순위떄문에 다르게 해석이 됩니다.

int *arr[3];
은 3개의 element를 가지는 array를 선언하며 각자는 int 가 들어있는 주소를 저장합니다.

(*ptr)[] 는
ptr 변수 하나를 선언하며, 그 변수가 가리키는 곳은 array([] 형태 단위) 입니다.

int arr[2][3];
이건 
arr[2][3] : arr로 시작주소를 가지는 2x3(6개)의 변수를 선언하며 해당 변수는 int 타입니다.

int (*arr[2])[3]; 
변수를 2개(array)로 선언하는데, 그 각각의 값은(*arr[])는 3개짜리 integer 수형에 대한 수소입니다.

array :   0   1   2   3   4   5   6   
            ^            ^
         arr[0]      arr[1]
            ^            ^
            p            p+1

int arr[2][3] => int (arr[])[3] => int (*arr)[3];
arr는 변하지 않는 상수이긴 하지만.....

USART 통신을 이용한 AVR로 hi~ 인사하기.

UART 통신을 연습하는 강좌에 올린 질문과 답변 연습 내용을 정리한 글입니다.


출처 : cafe.naver.com/laydigital/

안녕하세요, 통신을 위해서 사전에 준비할것들이 많다는것을 강좌를 통해 처음 알게되었습니다. 실생활에서 쓰는것만큼
편하게 되는게 아닌것을 처음으로 느껴보고 우리가 실생활에서 참 편하게 이용하는구나 라고 세삼 더 느끼게 됐네요...
준비해보면서 하나씩 이유에 대해서 알게되니 점점더 재미가 붙는것 같습니다. 과제가 있어 과제 연습용으로 수행코드를
작성해보았습니다.

터미널 에뮬레이터는 제일 간편한 하이퍼 터미널을 win7에서 실행하여 실습하였습니다. win7은 터미널이 없더군요....

------------------------------------------------------------------------
[과제] 바로 위에 있는 “I Like You!”와 똑 같은 화면이 나타나도록 하되, baudrate를 9600으로 하고 stop 비트를2비트, 패리티는 Even 패리티를 사용하여 프로그램해 보세요. ^^
------------------------------------------------------------------------


#include <avr/io.h>
#define kilo 1000UL
#define Mega kilo*kilo
#define F_CPU 16*Mega
//#define FOSC 16000000 //clock speed
//#define BAUD 115200
//#define Myubrr FOSC/16/BAUD-1  // FOSC/16/BAUD -1 + (-35%)
#include <util/delay.h>
#define NUL 0x00
void USART0_SET(uint16_t);
void USART0_Transmit (int8_t *p);

int8_t arr[] = {11,'I',0x20, 'L','I','K','E', 32, 'Y','O','U','!',11,13};
int8_t *ptr = arr;

int main(void)
{
 USART0_SET(103);  //baud rate 9600
  
    while(1)
    {
      USART0_Transmit(ptr); 
 }
}


void USART0_SET(uint16_t ubrr)
{
  /*set baud rate */
 UBRR0H = (uint8_t)(ubrr>>8);
 UBRR0L = (uint8_t) ubrr;
 /* Enable receiver and transmitter */
 UCSR0B = (0<<RXEN0) | (1<<TXEN0) | (0<<UCSZ02); 
   
 /*Set frame format : 8data, 2stop bit */
 UCSR0C = (1<<USBS0)|(3<<UCSZ00);
 /*Set Parity mode */
 UCSR0C |=(2<<UPM00); //enabled, even parity
}


void USART0_Transmit(int8_t *p)
{
 uint8_t i;
 for(i=0; i<(sizeof(arr)/sizeof(arr[0])) ; i++,p++)
 {
    /* Wait for empty transmit buffer */
 while( !(UCSR0A &  (1<<UDRE0)) )
 ;
 /* Put data into buffer, sends the data */

 UDR0 = *p;
    _delay_ms(50); //A confirmed
 }

}

코드 수행 결과물 


/////////////////////////////////////////////////////////////////////////////////////////////////////


nt8_t arr[] = {11,'I',0x20, 'L','I','K','E', 32, 'Y','O','U','!',11,13};

끝에 11,13은 '\n', '\r' 에 대한 문자 값인듯한데
11이 아니고 10입니다.

문자 string으로 
uint8_t *arr = "I Like You !\r\n";


void USART0_Transmit(int8_t *p)
{

while(*p)
{
/* Wait for empty transmit buffer */
while( !(UCSR0A & (1<<UDRE0)) ) ;
/* Put data into buffer, sends the data */

UDR0 = *p++;
_delay_ms(50); //A confirmed
}

}
이렇게 사용하기도 합니다. 
USART0_Transmit 에 문자열 주소를 받으면서도
global 변수 arr[] 을 통해 size를 얻는 형태가 조금 아쉽네요.. ^^

USART0_Transmit(arr, sizeof(arr)/sizeof(arr[0]));

void USART0_Transmit(int8_t *p, int len)
{

for(;len ; len --, p++)
{
/* Wait for empty transmit buffer */
while( !(UCSR0A & (1<<UDRE0)) )
;
/* Put data into buffer, sends the data */

UDR0 = *p;
_delay_ms(50); //A confirmed
}

이런 방식의 구현도 가능하겠죠.

광센서를 이용한 가로등 만들기 강좌

광선세를 이용해 가로등을 만들어보는 연습 코드와 관련한 질문을 정리한 글입니다.

출처 : cafe.naver.com/laydigital/

6-2. 해지면 켜지고 해뜨면 꺼지는 가로등
과제] ------------------------------------------------------------------

1.     100 Lux 정도에서는 2개의 가로등(LED)만 켜지고, 10 Lux 가 되면 4개, 1 Lux 가 되면 8개의 가로등(LED)이 켜지도록 함
2.     주위환경에 의하여 경계점에서 불이 꺼졌다 켜졌다하는 것을 방지하기 위하여 각 가로등은 일단 한 번 켜지면 어느 정도 수준까지 밝아지지 않으면 꺼지지 않고, 반대로 한 번 꺼지면 어느 정도 수준까지 어두어지지 않으면 켜지지 않도록 함(즉, 완충 공간을 설정)
 ------------------------------------------------------------------------

6-2 강좌에 있던 수행과제 입니다. 코드작성하면서 궁금증이 생겨서 질문 드립니다.
아래는 제가 과제조건에 맞추어 작성한 코드입니다.

질문1 은 아래 작성한 코드중에  while((ADCSRA & adc_start) != adc_start); // question?
이부분이 있는데요, 강좌에서는 (1<<ADIF)로 비트를 확인해서 시작과 끝을 확인해서 ADCH,ADCL data 를 read
하는데요, single mode 에서 바로 (1<<ADSC)를 사용자 set 해주고 시작시키면 conversion 완료시ADSC bit 자동으로 hardware clear된다고 해서 사용했는데 큰 무리가 없는지 일단 궁금합니다. 사용자가 conversion 중에 set 해도 영향을 주지않는다고 해서 괜찮다고 봤거든요.

질문2 는 C언어 문법에 대해  while((ADCSRA & adc_start) != adc_start);  이 mian 안에서 실행될때 실제로
int main()       ---- 1
{
  while(!)        ---- 2
  {
     single_conversion_mode()   ----- 3
  }  
}
위와 같은 구성에서 함수 3 안의 while((ADCSRA & adc_start) != adc_start); 는 함수3안에 있는 while문 아래 있는 code들은  실제로 while() { loop } 형태로 구성하면 원하는 구현동작을 실행시키지 않았습니다. 다른 if(){}형태도 마찮가지 이고 이 loop를 구성하면 뭔가 오작동이 일어나는데 뭔가 타이밍이 않맞는건지 왜그런지 몰라서 이유가 궁금해서 일단 질문으로 정리해봤습니다.

질문3  state_check() 함수를 만들어서 예전 올려주신 자료  [C+AVR 보충자료] high water / low water mark 를 참조해서 코드로 구현했는데  마음에 썩 들지가 않아서 좀더 괜찮은 표현형태나 방법이 있다면 알려주시면 대단히 감사하겠습니다.
물론 작동은 되는데 조건에 충실히 작동되는지 파악해보기 좋은 방법이 떠오르지 않아서 도움을 구합니다.

-------------------------------------------------------------------------------------------------------------------

#include <avr/io.h>

void init_adc_conversion(void);
//void alternative_func_set(void);
uint16_t single_conversion_mode(void);
uint8_t state_check(uint16_t val);

#define _1000lux 1000
#define _100lux 901
#define _10lux 871
#define _1lux 92
#define OFF 0
#define DAY 1
#define NIGHT 2
#define MidNight 3

#define  lights PORTA

struct lux_led
{
  uint16_t ref_val;
  uint8_t  led_num;
             
}light_senosr[]=
{
 {_1000lux,0x00},
 {_100lux,0x03},
 {_10lux,0x0f}, 
 {_1lux,0xff} 
};          //       cds: 10 lux - 35k,  100lux: 8k,  1lux: 2000k 

int main(void)
{
 uint16_t return_val;//,state=DAY;
 init_adc_conversion(); //adc initiate                        
 DDRA = 0xff; //led initiate

 struct lux_led *ptr= light_senosr;

 while(1)     //loop
 {
  return_val = single_conversion_mode();
  
  switch(state_check(return_val))
  {
   case OFF:      lights= ptr[OFF].led_num;      break;
   case DAY:      lights= ptr[DAY].led_num;      break;
   case NIGHT:    lights= ptr[NIGHT].led_num;    break;
   case MidNight: lights= ptr[MidNight].led_num; break;
  }
  
 }

}

void init_adc_conversion()
{
  ADMUX |= (0<<MUX4)|(0<<MUX3)|(0<<MUX2)|(0<<MUX1)|(0<<MUX0);  //AD0 enable.
  ADMUX &= ~(1<<ADLAR); //right present.
  ADMUX |= (0<<REFS1)|(0<<REFS0); //ADREF, internal  Vref turned off
  ADCSRA |= (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);  //adc: enable, ad prescale select 111: 128 
  ADCSRA &= ~(1<<ADFR); // ad free running mode 0: single, 1: enable. 
}


uint16_t single_conversion_mode()
{
   uint16_t adc_val,adc_regH;
   uint8_t adc_start, adc_regL;//,adc_flag;
  
   //adc_flag = (1<<ADIF);
   adc_start = (1<<ADSC);
   ADCSRA |= adc_start;           //in single, software give set signal ADSC bit for each single start.
  
   while((ADCSRA & adc_start) != adc_start); //question?
    //PORTA = 0x01;
     adc_regL = ADCL;   // low bit must read before high bit
     adc_regH = ADCH;   // if read ADCH bit conversion data first will update immediately
     adc_val = (adc_regH<<8)|(adc_regL);
     
   return adc_val;
   
}


uint8_t state_check(uint16_t val)
{
  uint16_t state = NIGHT;

  if (state == NIGHT)
  {
   if(900 <=val) state = DAY;
   
   else if((871>val) && (901>val) && (val>=871)) state = NIGHT;
   
   else if ((92<val)&&(871>val)) state = MidNight;
   
  }
  
  if(state == DAY) 
  {
   if((901<val) && (901>=val) && (val>871)) state = DAY;
         
   else if(871>=val) state = NIGHT;
   
  }
  
  if(901<val) state = OFF;

  return state;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

1. ADC 를 동작시키는 ADCSRA의 
비트 6 (ADSC)는 adc 변환을 시작 시키는 비트이고
종료를 체크하는건 비트 4(ADIF) 입니다.
single conversion 모드시에는
ADCSRA |= (1 < ADSC);
while(ADCSRA & (1 << ADIF));
ADCSRA |= (1<< ADIF);
이게 정상적인 과정입니다.
ADIF 는 해당 비트를 1로 쓰기 하거나, 또는 ADC 인터럽트 루틴이 enable 되어서 인터럽트 루틴으로
진입할때 clear 됩니다.

ADSC비트를 체크하도록 코딩하신것 같은데요?

2. ADIF flag를 체크하도록 코딩하시면 되겟죠.

uint8_t state_check(uint16_t val)
{
uint16_t state = NIGHT;

state 변수를 위처럼 선언하면 매번 함수 호출시마다 NIGHT가 됩니다.
해당변수를 state_check에서만 사용하고 계속 그 값이 유지 되게 하실려면

static uint8_t state = NIGHT;
// 8비트로도 충분하므로


if (state == NIGHT)
{
if(NIGHT_TO_DAY <= val) state = DAY;

// 이 줄은 필요없음.. else if((871>val) && (901>val) && (val>=871)) state = NIGHT;

else if ((92<val)&&(871>val)) state = MidNight;

}

if(state == DAY) 
{
if((901<val) && (901>=val) && (val>871)) state = DAY;

else if(871>=val) state = NIGHT;

}

uint8_t state_check(uint16_t val)
그리고 이 함수가 3개의 상태에 대한 코드로 작성되어 있지 않네요(midNight에서 전환하는 
코드가 없음)

모터를 이용하여 자연풍 선풍기를 돌려보자.

모터 구동 강좌의 연습 코드 입니다.

출처 : cafe.naver.com/laydigital/

5-3. 솔솔 자연풍 선풍기 [과제수행]

안녕하세요, 이번 과제를 욕심을 내서 해보았는데 의도한바를 다 적용해보지 못해 아쉬운 부분이 많습니다.
진도를 나가기 위해 아쉽지만 과제수행으로 올리니 살펴보시고 개선 사항이나 필요한 부분이 보인다면 마음껏
알려주시면 대단히 감사드리겠습니당.

[과제] ------------------------------------------------------------------
SW1을 한 번 누르면 미풍, 두번 누르면 약풍, 세번 누르면 강풍, 네번 누르면 다시 미풍이 되며, SW2를 누르면 정지가 되는 선풍기를 프로그램해 보세요. (집에서 쓰는 선풍기처럼 말이지요.)
------------------------------------------------------------------------
추가: 1. 구조체를 이용하여 꾸며 보자. (sigled list 형태를 취해보자) 
        -> 스위치 없이 해보았는데 스위치 포함하면 의미가 없어 보여서 list 형태를 빼고 구조체 형태로 구성 
        2. Num_click : one click, double click, triple click 으로 작동 (일정 시간 동안 증가한 클릭수)
        -> 중간 진행하다 코드가 의도와 다르게  애매해서  매 클릭시마다 상태를 변경으로 수정
-----------------------------------------------------------------------------------
질문1. 아래 구성에서 Timer2의 OCn 신호를 제어하기 위해 Datasheet 상에 명시된 조건중에 DDRn 값의 상태에 따라 Output 신호의 출력 상태가 정해진다는 내용을 보고 DDRn값으로 신호의 출력 상태를 제어하고 상태에 따라 OCRn 값을 주는 식으로 구성하였는데 괜찮은 방법인지 잘모르겠어서 질문 드려봅니다. 이방법외에도 OCn의 신호 출력 상태를
제어 할수있는 방법이 있다면 소개해주시면 적용해 보겠습니다.

질문2. 제가 추가 해보려했던 추가사항중 버튼+singled list 형태나 Num_click 중 추가해볼수있는 코드형태가 딱히 않떠오르는데요 힌트가 있으시면 알려주시면 감사하겠습니다.

------------------------------------------------------------------------------------------------
/* MCU_init: using timer2(fast pwm mode), external interrupt alternative port function. */
/* CODE: struct & singled list*/

#include <avr/io.h>

#define kilo 1000UL
#define Mega (kilo*kilo)
#define F_CPU 16*Mega 
#include <util/delay.h>
#include <avr/interrupt.h>

#define ST0P  0
#define GO    1

#define MODE1 0
#define MODE2 1
#define MODE3 2
#define NULL 0

void current_motor(void);
void current_sw(void);
void mcu_timer2_fPWM(void);
//void mcu_timer0_ctc(void);
void mcu_exinterrupt_switch(void);
void chattering(char);

struct op
{
  volatile uint8_t state; 
  volatile uint8_t mode_sel; 
  uint8_t array_ocr2[];      
  //struct op *next;
  
}motor_op =
{
 ST0P,     
 MODE3,   //after reset "MODE3" trans to "MODE1" by INT4
 { 76,156,220 } //TCNT2(256),30%,60%,90%
};

int main()
{
  current_motor();
  current_sw();
  mcu_timer2_fPWM();
  mcu_exinterrupt_switch();
    
  struct op *ptr = &motor_op;
  DDRA = 0xff;
  
 while(1)
 {
   
  if(ptr->state)
  {
      switch(ptr->mode_sel)
      {
       case MODE1:  OCR2 = ptr->array_ocr2[0]; PORTA=0x10;  break;
       case MODE2:  OCR2 = ptr->array_ocr2[1]; PORTA=0x20;  break;
       case MODE3:  OCR2 = ptr->array_ocr2[2]; PORTA=0x40;  break;
      }
   
  
   }else   {
    PORTA ^= 0x01;
    _delay_ms(100);
   }
  }
}


/*-----------------Timer2 & Motor section------------------------*/
void current_motor(void)
{
   DDRB = (1<<DDB6)|(0<DDB7); //for use OC2 (1<<DDB7)  //enabled by ISR(INT4_vect).
}

/*----------Timer02_8bit--------------------*/
void mcu_timer2_fPWM(void)
{
/*--Fast PWM---*/
TCCR2 = (1<<WGM21)|(1<<WGM20)|(1<<COM21)|(0<<COM20)|(1<<CS22)|(0<<CS21)|(1<<CS20); //OC2: enable (COM21:0 =2)
        //(fast mode)         ( clear:up, set:down)   (clock : 1024) = small motor didn't respond high frequency
/*--OCF2-------*/    
//TIMSK = (1<<OCR2);  //timer2: compare match interrupt enable. //ISR(TIMER2_COMP_VECT) : enable.
}

/*--ISR OCF2-------*/      
ISR(TIMER2_COMP_vect)  
{

  /*none*/

}


/*------------ External interrupt  --------------*/
void current_sw(void)
{
   DDRE = 0xff;
   DDRE ^= (1<<DDE4)|(1<<DDE5);  //default: 0  //use for INT4,5 port pin function enable.
}
/*-------------(INT4,INT5)-----------------*/
void mcu_exinterrupt_switch(void)
{
 EIMSK = (1<<INT4)|(1<<INT5);  // INT4,5 enable.
 EICRB = (1<<ISC41)|(0<<ISC40)|(1<<ISC51)|(0<<ISC50); //interrupt source collect, trigger: rising edge.
 sei(); 
}
void chattering(char num)
{
  EIFR= (1<<num);
  _delay_ms(30);

}
/*-------------- (ISR, INT4)---------------*/  //use trans array table.
ISR(INT4_vect)                 
{                            // mode1 mode2 mode3
   static uint8_t trans_mode[3] = {MODE2,MODE3,MODE1};  
   motor_op.mode_sel = trans_mode[motor_op.mode_sel];    
                                   //stop  go
   static uint8_t trans_array[2] = { GO, GO };
   motor_op.state = trans_array[motor_op.state];     
   DDRB |= 1<<DDB7; //motor: start. (DDR: connect OC2 signal)
   chattering(4);
}
/*-------------- (ISR, INT5)---------------*/ //use trans array table.
ISR(INT5_vect)                 
{                                 //mode 1 mode2 mode3
   static uint8_t trans_mode[3] = {MODE3,MODE3,MODE3};
   motor_op.mode_sel = trans_mode[motor_op.mode_sel];
                           // stop go
   static uint8_t tran_array[2] = {ST0P,ST0P};
   motor_op.state = tran_array[motor_op.state]; 
   DDRB &= 0<<DDB7; // motor: stop (DDR: cut OC2 signal)
   OCR2 = 0;
   chattering(5);
}




/////////////////////////////////////////////////////////////////////////////////////////////////////

DDRB &= 0<<DDB7; // motor: stop (DDR: cut OC2 signal)

해당 비트만 처리 하실려면
DDRB &= ~(1 << DDB7);
이렇게 해야 합니다.

물론 위의 코드는
DDRB &= 0 << DDRB7;
=
DDRB &= 0;
DDRB의 모든 비트를 0으로 바꿉니다.

oid chattering(char num)
{
EIFR= (1<<num);
_delay_ms(30);

}
EIFR과 _delay_ms 간 순서를 바꿔야 의미가 있습니다.

EIFR은 하나의 인터럽트 마다 하나씩 기억하는 flag입니다.

버턴 눌러짐 이후에 30 ms 후까지 기다린 다음 
그 사이에 발생한 다수의 인터럽트가 EIFR에 설정된걸 지우는 겁니다.

외부 인터럽트를 이용하는 경우 누를때 외에 버턴을놓을때도 채터링이발생하므로 
완전한 해결을 할려면

버턴은 인터럽트로 처리하지 않고

10ms 주기의 타이머에서 버턴의 상태를 보고
일정시간 low일때 누름 판단하는게 좋습니다.

tick_10ms()
{
if( button1_pressed() && button_cnt != 255) {
if(++button_cnt >= 5) // 50 ms 이상 눌러짐
{
눌러질때 처리할것..
button_cnt = 255; // 누른 상태임을 기억
}
}
if( !button1_pressed()) button_cnt = 0;
}

상태 machine으로도 구현가능한데, 위 코드는 
버턴 누른 상태를 button_cnt 변수의 최대 값으로 대신해서
state machine 없이 한겁니다.
그래도 state machine을 많이 연습하는게 나중에도 도움이 될겁니다.
가능하면 state machine으로 해 보세요.

모터를 돌려 봅시다 강좌 질문

모터 구동과 과련된 강좌에 질문내용과 답변을 정리한 글입니다.

출처 : cafe.naver.com/laydigital/

5-2. 모터를 돌려봅시다 C + AVR + 임베디드 여행
안녕하세요 오랜만(?)에 올리는 연습과제 수행입니다.
목적은 구조체를 사용하여 구현해보자 였습니다만...알려주셨던 구조체 연습 코드를 조금 변형하는 수준에서 멈췄습니다...;

[과제] ------------------------------------------------------------------
[과제1] SW1을 누르면 모터가 정방향으로 돌고, SW2를 누르면 정지하는 프로그램을 작성해 보세요.
[과제 2] [과제 1]의 프로그램에 모터의 상태가 바뀔 때마다, FND에 “STOP”, “GO”가 디스플레이되도록 프로그램을 추가해 보세요.
------------------------------------------------------------------------
위의 과제를 추가하여 아래와 같이

FND: STAN 표시:  모터 준비.
SW1 를 한번 누르면 FND: GOOO 회전
SW1 를 다시 누르면 FND: REVE 역회전
SW2 를 누르면 각상태에 상관없이 정지 FND: STOP 로 바꾸어 구성해 보았습니다.

질문1) 아래 코드 중 빨간색 switch~case 문을 한번만 써서 수행 해보고자 했지만 표현의 한계를 느끼고
2개를 사용하여 구성하게 됐습니다;;. 좀더 효율적인(?) 표현 방법이 떠오르지않아 고민하다 C구문 표현력의 한계를
느끼고 수행코드를 올리오니 살펴보시고 개선할 부분이나 보충할 부분이 보이시면 여과없이 알려주시기 바랍니다.


질문2) 각 SW 초기 상태는 PUSH(X): HIGH(1), PUSH(O): LOW(O)
EICRB = (1<<ISC41)|(1<ISC40)|(1<<ISC51)|(1<<ISC50);// 00:low_act, 01:logic_act, 10:falling_act, 11:rising_act
위와 같이 선언하였는데 sw1은 의도하지않게 falling(1->0)으로 동작하고 sw2만 rising(0->1) 형태로 동작하는데
문제점을 찾아볼려고 애썼지만 결국 못찾아서 질문으로 올립니다.


수행 코드.
/* MCU library setting */
#include <avr/io.h>  // i/o 
#define kilo 1000UL
#define mega (kilo*kilo)
#define F_CPU 16*mega    // necessary define for delay function (must)
#include <util/delay.h>  // library function for delay
#include <avr/interrupt.h>  //using for external interrupt 

/*Motor state define*/
#define STANDBY 0
#define GO      1
#define STOP    2
#define REVERSE 3

/*FND display*/
#define DSP_DATA PORTC
#define DSP_FIGU PORTG

/*FND Font*/
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define G 5
#define N 6
#define O 7
#define P 8
#define R 9
#define S 10
#define T 11
#define V 12
#define Y 13


void init_switch(void);
void init_motor(void);
void init_fnd(void);
void fnd_dsp(char val);
volatile uint8_t STATE_SW1=STANDBY;  //global Variable for external interrupt sw1
//volatile uint8_t STATE_SW2=STANDBY;  
uint8_t fnd_font[]={0x77,0x7f,0x39,0x3f,0x79,0x7d,0x37,0x3f,0x73,0xf7,0x6d,0x31,0x72,0x66}; //a,b,c,d,e,g,n,o,p,r,s,v,y


struct _motor_op
{
   uint8_t sw_state;
   uint8_t stop;
   uint8_t revese;
   uint8_t go;
   uint8_t standby; 
}motor_op[]=
 {
   {STANDBY,S,R,G,S},
   {GO     ,T,E,O,T},
   {STOP   ,O,V,O,A},       
   {REVERSE,P,E,O,N}
          
 };

int main(void)
{
    init_switch();
   init_motor();
   init_fnd();
 while(1)
 {
       cli();
       switch(motor_op[STATE_SW1].sw_state)  //switch(STATE_SW1)
 {
        case GO      : PORTB = (0<<PB6)|(1<<PB7),fnd_dsp(GO) ; break;
        case REVERSE : PORTB = (1<<PB6)|(0<<PB7),fnd_dsp(REVERSE) ; break;
        case STOP    : PORTB = (1<<PB6)|(1<<PB7),fnd_dsp(STOP) ; break;
        case STANDBY : PORTB = (0<<PB6)|(0<<PB7),fnd_dsp(STANDBY) ; break;
       }
        sei(); // 채터링 방지목적..
 }   
}

void init_switch(void)
{
 DDRE = (0<<DDE4)|(0<<DDE5);   // switch current input 
 EIMSK = (1<<INT4)|(1<<INT5);  // external interrupt enable
 EICRB = (1<<ISC41)|(1<ISC40)|(1<<ISC51)|(1<<ISC50);// 00:low_act, 01:logic_act, 10:falling_act, 11:rising_act
 sei(); 
}

void init_motor(void)
{
    DDRB = (1<<DDB6)|(1<<DDB7);    // motor current output
}

void init_fnd(void)
{
    DDRC = 0xff;    //display_data
 DDRG = 0xff;    //display_figure
}

void fnd_dsp(char val)
{
 uint8_t i;

 for(i=0; i<4; i++)  //20ms
 {
  
  switch(val)
  {
  case GO      : DSP_DATA = fnd_font[motor_op[i].go]; break;
  case REVERSE : DSP_DATA = fnd_font[motor_op[i].revese]; break;
  case STOP    : DSP_DATA = fnd_font[motor_op[i].stop]; break;
  case STANDBY : DSP_DATA = fnd_font[motor_op[i].standby]; break;
  }
  DSP_FIGU = (0x08>>i);
  _delay_ms(5);  // 5ms
 }
  }


ISR(INT4_vect) //external_sw1
{
   if(STATE_SW1==STANDBY || STATE_SW1 == STOP)
 STATE_SW1 = GO;
   else if(STATE_SW1 == GO)
    STATE_SW1 = REVERSE; 
   else if(STATE_SW1 == REVERSE)
    STATE_SW1 = GO;  
}

ISR(INT5_vect) //external_sw2
{
   if(STATE_SW1 == GO)
    STATE_SW1 = STOP;
   else if( STATE_SW1 == REVERSE)
    STATE_SW1 = STOP; 
   else if(STATE_SW1 == STOP || STATE_SW1 == STANDBY)
    STATE_SW1 = STANDBY;  
}







EICRB = (1<<ISC41)|(1<ISC40)|(1<<ISC51)|(1<<ISC50);// 00:low_act, 01:logic_act, 10:falling_act, 11:rising_act

1 < ISC40 
=>
1 << ISC40



struct _motor_op
{
uint8_t sw_state;
uint8_t stop;
uint8_t revese;
uint8_t go;
uint8_t standby; 
}motor_op[]=
{
{STANDBY,S,R,G,S},
{GO ,T,E,O,T},
{STOP ,O,V,O,A}, 
{REVERSE,P,E,O,N}

};
이 구조체에서 
sw_state와
stop,revese,go,standby 간에 연관성이 있나요?

스위치 각각이 현재 상태가 따로 운용되지 않아 보입니다.
구조체는 switch(button)의 상태라기 보다

motor 의 상태로 보입니다.

struct _motor_context_t {
uint8_t status;
struct {
uint8_t map[4];
} fnd_map[4]; // 0 : stop, 1 : reverse, 2 : go, 3 : standby
} motor_state =
{
STANDBY,
/* fnd_map[0] for stop */
{S,T,O,P },
{R,E,V,E },
{G,O,O,O},
{S,T,A, N}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////


/* MCU library setting */
#include <avr/io.h>  // i/o 
#define kilo 1000UL
#define mega (kilo*kilo)
#define F_CPU 16*mega    // necessary define for delay function (must)
#include <util/delay.h>  // library function for delay
#include <avr/interrupt.h>  //using for external interrupt 

/*Motor state define*/
#define STANDBY 3
#define GO      2
#define REVERSE 1
#define STOP    0

/*FND display*/
#define DSP_DATA PORTC
#define DSP_FIGU PORTG

/*FND Font*/
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define G 5
#define N 6
#define O 7
#define P 8
#define R 9
#define S 10
#define T 11
#define V 12
#define Y 13


void init_switch(void);
void init_motor(void);
void init_fnd(void);
void fnd_dsp(char val);

//volatile uint8_t STATE_SW2=STANDBY;  
uint8_t fnd_font[]={0x77,0x7f,0x39,0x3f,0x79,0x7d,0x37,0x3f,0x73,0xf7,0x6d,0x31,0x72,0x66}; //a,b,c,d,e,g,n,o,p,r,s,v,y

struct _motor_context_t {
    volatile uint8_t status;
    uint8_t map[4][4];
     // 0 : stop, 1 : reverse, 2 : go, 3 : standby
} motor_state =
{
    STANDBY,
/* fnd_map[0] for stop */
    { {S,T,O,P },
      {R,E,V,E },
      {G,O,O,O },
      {S,T,A, N}
 }
};


int main(void)
{
   init_switch();
   init_motor();
   init_fnd();
   DDRA = 0xff;
   PORTA = 0xff;
   _delay_ms(1000);
   while(1)
  {

       switch(motor_state.status)  //switch(STATE_SW1)
        {
        case GO      : PORTB = (0<<PB6)|(1<<PB7),fnd_dsp(GO) ; break;
        case REVERSE : PORTB = (1<<PB6)|(0<<PB7),fnd_dsp(REVERSE) ; break;
        case STOP    : PORTB = (1<<PB6)|(1<<PB7),fnd_dsp(STOP) ; break;
        case STANDBY : PORTB = (0<<PB6)|(0<<PB7),fnd_dsp(STANDBY) ; break;
       }

 }   
}

void init_switch(void)
{
 DDRE = (0<<DDE4)|(0<<DDE5);   // switch current input 
 EIMSK = (1<<INT4)|(1<<INT5);  // external interrupt enable
 EICRB = (1<<ISC41)|(1<<ISC40)|(1<<ISC51)|(1<<ISC50);// 00:low_act, 01:logic_act, 10:falling_act, 11:rising_act
 sei(); 
}

void init_motor(void)
{
    DDRB = (1<<DDB6)|(1<<DDB7);    // motor current output
}

void init_fnd(void)
{
    DDRC = 0xff;    //display_data
    DDRG = 0xff;    //display_figure
}

void fnd_dsp(char val)
{
 uint8_t i;

  for( i = 0 ; i < 4 ; i ++)
  {
      DSP_DATA = fnd_font[motor_state.map[val][i]]; DSP_FIGU = 8 >> i; _delay_ms(5);
  }
}

ISR(INT4_vect) //external_sw1
{
/*                          cur_state STOP, REVERSE, GO,     STANDYBY */
   static uint8_t state_trans[4] =   {GO   ,GO      ,REVERSE,GO};

   motor_state.status = state_trans[motor_state.status]; 

 _delay_ms(50);
 EIFR = 1 << 4;

}

ISR(INT5_vect) //external_sw2
{
/*                          cur_state STOP,      REVERSE, GO,     STANDYBY */
   static uint8_t state_trans[4] =   {STANDBY   ,STOP    ,STOP,   GO};

   motor_state.status = state_trans[motor_state.status]; 

 _delay_ms(50);
 EIFR = 1 << 5;

}

인터럽트 루틴에서는 각자 state_trans[] 가 있고
현재 상태를 넣으면 다음 상태를 알려주는 상태 천이 테이블입니다.
sw2인 경우
state_trans[STOP] = STANDBY 입니다.

즉 STOP에서 버턴이 눌러지면 STANDBY 를 리턴하는 테이블입니다.


Total Pageviews