[아두이노] LCD16x2 I2C(LiquidCrystal_I2C) 제어

IOT/아두이노|2019. 2. 25. 09:00

[아두이노] LCD16x2 I2C(LiquidCrystal_I2C) 제어



어제 가상시뮬레이터에서 LCD 16x2를 제어를 해보았는데 오늘은 실제 LCD 16x2 I2C을 이용해서 동일한 코딩 로직으로 결과를 출력하는 실험을 해보겠습니다. 사실 LCD 16x2를 가지고 있어 그것도 보여드리면 좋았겠지만 그냥 편하게 실험하기 위해서 LCD 16x2 I2C를 사용해서 실험 했네요.

1. LCD 16x2 I2C



LCD 16x2 I2C의 뒤면 검은색 모듈이 추가되어 있는데 I2C 통신을 하기 위한 모듈입니다. 간단히 4핀으로 구성되어 있으면 아두이노에 연결할때도 그리 어렵지 않게 연결할 수 있습니다.


제가 소유한 LCD 16x2와 LCD 16x2 I2C 인데 보는것 같이 핀 4개짜리를 사용하는게 훨 편하겠죠.

LCD 16x2 I2C 핀 4핀으로 구성

  • Gnd
  • Vcc(5V)
  • 데이터핀(A4)
  • 클럭핀(A5)

LCD 16x2 I2C의 뒷면에 보시면 적혀있기 때문에 쉽게 연결 할 수 있습니다.

LCD 16x2 I2C 주의사항

  • 부품마다 주소가 다를 수 있다. : 제가 사용한 부품의 주소는 LiquidCrystal_I2C 라이브러리 에서 이 클래스를 사용하기 위해서 사용하는 LCD 16x2 I2C의 주소값이 필요한데 처음에 작동을 안해서 불량품인 줄 알았어요. 구글링해서 주소가 따로 있고 다르면 출력이 안된다는 사실을 나중에 알게 되었죠.
  • 가변저항을 조설해야 한다. : 파란색 작은 사각모형에 십자나사 모양이 있는데 첨에는 한쪽으로 감겨져 있어서 이걸 돌릴 생각없이 LCD에 불이 안들어와서 불량품인 줄 알고 삽질을 한 기억이 있네요. 이걸 돌려서 밝기를 조절해야하는데 처음 만질때는 생각을 못했네요.

LCD 16x2 I2C 주소 확인법

아두이노에서 I2C에 잡혀있는 주소를 알아볼 수 있습니다. 예제가 있으니 그걸 복사하셔서 자신이 사용하는 부품의 주소를 확인하시면 됩니다.

LCD 16x2 I2C 데이터 시트

이곳에 가셔서 LCD 16x2 I2C의 데이터 시트를 확인하시면 됩니다. 아주 간단하게 소개되어 있어서 보기 편할꺼에요. 이곳에 가시면 이표가 있는데 LCD 16x2 I2C 주소가 어떻게 지정되는지 확인 할 수 있습니다.


위에 LCD 16x2 I2C 뒤면 사진에서 파란사각모양(가변저항) 밑에 3개의 작은 사격형 모양으로 모듈 회로에 표시되어 있을꺼에요 각각 A0, A1, A2로 납땝이 어떤식으로 되어 있느냐에 따라서 주소가 바뀌게 됩니다. 인두가 있는분들은 주소를 변경해 볼 수 있겠죠. 그런데 별로 추천드리지 않고요 그냥 만들어져 있는 형태로 실험하세요.

하지만 2개이상을 쓸때 같은 주소인데 2개가 다른 용도로 개별적으로 출력을 하고자 할때는 한개 주소를 변경해줘야 겠죠. 변경해주면 해당주소에 해당값만 출력되게 됨으로 여러개를 같은 라인에 연결하여 사용할 수 있겠죠.

2. 회로도 구성


  • 준비물 : LCD 16x2 I2C 1개, 아두이노우노
  • 내용 : LiquidCrystal_I2C 라이브러리를 사용하며 값을 출력하기 위해서 LCD 16x2 I2C 회로도를 표현을 하자.


상단에 표시한 아무것도 적혀있지 않는 핀 두개가 있습니다. 거기 핀은 A5, A4라고 참고로 알아두시면 돼요. 위로 연결을 하던 아래로 연결을 하던 동일하니깐 아무곳이나 연결하세요.

3. 코딩



LiquidCrystal 함수

LiquidCrystal_I2C lcd(0x3F,16,2);  // 0x27 or 0x3F
  • LiquidCrystal_I2C lcd(주소,가로,세로) : LCD 16x2 I2C 부품의 주소와 출력사이즈값을 지정합니다.

이전 실험해서는 LiquidCrystal(rs, enable, d4, d5, d6, d7) 함수만 사용하였지만 LiquidCrystal_I2C클래스에는 생성자 함수 인자가 주소랑 가로x세로 값을 던져 줍니다. LiquidCrystal 클래스처럼 함수 선택장애는 없겠죠. 그외 함수들은 대충 비슷함으로 생략합니다.

설계는 이전 LCD16x2 (LiquidCrystal) 제어(아두이노) 소스랑 동일합니다. 동일한 형태로 진행했습니다.

혹시 함수를 자세히 보고 싶으시다면https://github.com/marcoschwartz/LiquidCrystal_I2C 가셔서 LiquidCrystal_I2C.h 파일을 살펴보세요. 그리고 함수 내부동작을 자세히 보기 위해서는 LiquidCrystal_I2C.cpp 파일을 열어보시면 됩니다.

코딩을 전체적으로 하면

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x3F,16,2);  // 0x27 or 0x3F

void setup()
{
  lcd.init(); //초기화
  lcd.backlight(); //배경불켜기
  lcd.setCursor(0, 0);
  lcd.print("Hello World");
  delay(1000);
  lcd.clear();
}

void loop() {
  lcd.setCursor(0, 1);
  lcd.print("COUNT : ");
  for(int i=0;i<10;i++){ 
    lcd.setCursor(9, 1);
    lcd.print(i);    
    delay(1000);    
  }    
}

4. 라이브러리 추가하기


우선 LiquidCrystal_I2C 라이브러리를 사용하기 위해서는 아두이노 IDE에 라이브러리로 등록해 줘야 합니다. 나중에 다른 부품을 사용할때도 이 과정을 계속 수행해야 하기 때문에 잘 알아 두세요.

  • PC에 아두이노 IED 설치된 경우


라이브러리 관리를 누르고 검색어로 LiquidCrystal_I2C를 치시면 설치가능한 라이브러리 파일들이 나타납니다. 이렇게 설치해도 되고요 구글링하셔서 해당 라이브러리파일을 다운 받은 후 .zip 라이브러리추가하시면 됩니다. 그러면 파일->예제 열기가 있는데 추가되어 있을꺼에요. 그 예제로 실험하셔도 됩니다.

  • RaspberryPi3에다 아두이노 IED를 설치된 경우

제가 RaspberryPi3에 아두이노 IED를 설치해서 아두이노를 원격으로 실험하고 있는데요. 문제는 라이브러리 관리 항목이 없어서 일일히 다운 받아야 하는게 귀찮더군요.

구글 검색을 통해 해당 라이브러리 파일이 있는 GitHub 경로를 찾아야 합니다.


이렇게 페이지에 들어가면 Clone or download를 클릭하시면 경로가 나오고 그걸 복사를 합니다. 그리고 라이브러리파일을 저장할 디렉토리를 만든 후 그 위치에서 다운받습니다.

git clone https://github.com/marcoschwartz/LiquidCrystal_I2C.git

그리고 나서 아두이노 IED에서 Add Libray... 를 눌러 다운받았던 경로의 라이브러리파일을 등록해야 사용할 수 있습니다.


파일->예제 누르니 3개의 샘풀 예제가 등록되어 있네요. HelloWorld 예제로 실험하시거나 아니면 위 코딩으로 정상적으로 출력되는지 복사하셔서 실험하시면 됩니다.

5. 결과


동영상 결과는 간단히 실제 결과만 출력되는 걸 보여줍니다. 여기에 코딩된 것은 실행 시키고 달려가서 아두이노우노가 있는 위치까지 가서 촬영을 해야 했기 때문에 "Hellow World"의 출력 메시지를 최대한 딜레이 시간을 준 코딩으로 촬영 했네요.

마무리


오늘은 가상시뮬레이터에서는 실험 할 수 없습니다. 어제 LCD 16x2 로 출력은 가능했지만 실제 LCD 16x2 I2C가 가상시뮬레이터에는 없기 때문에 아쉽게도 오늘 실험은 실제 아두이노 부품이 있는분만 가능할 듯 싶네요.

대충 이런식으로 제어를 한다는 것만 보여 드렸으며 자세한 함수 설명은 생략했습니다. 계속 가상시뮬레이터 실험을 주된 실험으로 이루어지기 때문에 가상시뮬레이터에만 초점을 두겠습니다.

이제 LCD 16x2 로 출력을 할 수 있게 되면은 측정센서 부품을 사용할때 그 결과를 LCD 16x2로 출력시키면 좀 더 재미 있겠죠.


댓글()

[아두이노] LCD16x2 (LiquidCrystal) 제어

IOT/아두이노|2019. 2. 24. 17:07

[아두이노] LCD16x2 (LiquidCrystal) 제어



오늘은 LCD 16x2 에 값을 출력하는 실험입니다. 회로도는 arduino.cc에서 제공되는 샘플을 그대로 따라 실험을 하였습니다. 저도 LCD 16x2 부품을 사용할때마다 이전에 실험했던 디자인을 찾아서 보고 회로도를 표현만드네요. 우선 사용하는 핀이 많아서 좀 햇갈리고 잘못 표현할 수 있어서 그냥 예제의 표현을 기반으로 가상시뮬레이터로 정상적으로 결과가 나오는지 실험해 보는걸로 하겠습니다.

1. LCD 16x2



15, 16핀은 백그라운드 밝기라서 220옴을 달았더군요. 1번 Gnd, 2번 Vcc, 3번 Vo로 여기에 출력 글자의 밝기가 결정되더군요.
DB0~7까지 데이터 핀이고, 나머지 RS, R/W, Enable핀으로 구성되어 있습니다.

2. 회로도 구성


  • 준비물 : LCD 16x2 1개, 저항 220옴 1개, 10k 가변저항기 1개, 아두이노우노, 뻥판
  • 내용 : LiquidCrystal(rs, enable, d4, d5, d6, d7) 을 사용하여 값을 출력하기 위해서 16x2 회로도를 표현을 하자.
  • 인용자료 : https://www.arduino.cc/en/Reference/LiquidCrystal(아무 예제나 클릭해도 됨)

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 9, 10, 11, 12);

우선 이 함수를 사용하기 위해서 rs, enable핀과 데이터 데이터 4,5,6,7번 핀을 사용을 해야합니다. 회로도 디자인은 적당한 핀으로 디자인이 깔끔하게 보이게 하기 위해서 3, 4, 9, 10, 11, 12핀에 연결했습니다. 그리고 15(Vcc)번과 16(Gnd)핀이 있는데 15번에 220옴을 붙여서 적당히 백그라운의 밝기를 고정시켰습니다. 그리고 전원으로 1번은 Gnd 2번 Vcc로 연결하는데 3번에 10k 가변저항을 붙여서 글자의 밝기를 조절하도록 배치된 회로도 만들어 보도록 하겠습니다.


우선 가장 심풀하게 표현된건데 사실 실제로 LCD 16x2를 구매하실때 이 부품을 잘 안사고 LCD 16x2 I2C 를 이용합니다. LCD 16x2 뒷면에 I2C모듈이 붙어 있어서 아두이노에서 필요한 핀이 2개뿐이 안필요해서 여러개의 부품 제어때 LCD 16x2 I2C를 주로 사용합니다. 기본 제어 부품들이 LCD 16x2 뒷면에 모듈로 다 붙어 있어서 따로 복잡하게 회로도를 만들 필요가 없어서 많이 사용합니다.


총 4핀에서 전원핀 2개를 제외하면 아두이노에서 사용할 2핀만 연결하시면 됩니다. 힘들데 선을 연결할 필요없이 4개의 핀만 연결하니 실제로 실험할 경우 저 부품을 사서 실험하겠죠. 문제는 가상시뮬레이터에서는 힘들게 선을 다 배치해야 합니다. 하지만 선을 배치함으로 LCD 16x2의 부품에 대해서 보다 자세히 이해할 수 있기 때문에 귀찮아 하지 마시고 한번 디자인 해보세요.

3. 코딩



LiquidCrystal 함수

LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal(rs, rw, enable, d4, d5, d6, d7) 
LiquidCrystal(rs, enable, d0, d1, d2, d3, d4, d5, d6, d7) 
LiquidCrystal(rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7)

아두이노의 핀 회로도 배치가 달라지겠죠. 실험해서는 LiquidCrystal(rs, enable, d4, d5, d6, d7) 함수만 사용합니다.

그외 함수들

제블러그에서 대충 함수를 요약한 건데 가져왔네요.https://www.arduino.cc/en/Reference/LiquidCrystal 보시면 오른쪽에 함수목록들이 있는데 가서 함수들을 읽어보시면 됩니다. 간단히 요약하면은

begin(가로,세로) => 화면나누기
setCursor(가로,세로) => 커서의 위치
print(출력문자열) => 출력
write(출력문자) => 출력
clear() => 화면지우기
noDisplay() => 끄기
display() => 켜기
scrollDisplayRight() => 오른쪽으로 한칸이동
scrollDisplayLeft() => 왼쪽으로 한칸이동
autoscroll() => 자동으로 왼쪽이동
lcd.createChar(0, 문자배열값) => 직접 문자를 만듬(write(byte(0))출력)

외우지 마시고 대충 정리했다가 LCD 16x2를 사용하실때 이 함수들을 적어놨다가 사용하시면 됩니다.

설계

제 블로그에서 간단히 실험한 예제을 그대로 사용했네요. LCD 16x2에 "Hello World"를 출력해보고 "COUNT : 숫자"로 출력되게해서 카운터를 세보자.

  1. LiquidCrystal를 사용하기 위해서 객체를 선언해야지. 그런데 클래스 보니깐 선언때 생성자함수이네! 사용할 핀을 넘겨줘야 하는군!
  2. 초기작업이 끝났으니 이제 lcd 객체변수로 화면을 16x2로 세팅해야지
  3. 이제 LCD 16x2의 값을 출력할 커서의 위치는 잡아야 줘야지.
  4. "Hello World" 출력해야지
  5. 다음 "COUNT: 숫자"로 카운트를 세면서 값을 출력해야지. 그런데 그냥 출력하면 기존의 쓰여진 값에 덮어씌워지니깐 clear()함수로 화면을 지워야지
  6. "COUNT: 숫자" 숫자를 매번 clear하고 지우기 그러니깐 "COUNT : "한번만 하고 숫자만 갱신하는게 실속 있겠다. 그러면 바로 카운터 숫자가 나오는 위치만 갱신하면 되겠죠. 아까 글자가 출력할 커서의 위치를 지정하는 함수가 있었죠. 그 함수로 숫자의 위치 "COUNT : "은 공백포함 8자리니깐 setCursor(9, 1)를 이용해서 갱신하면 쉽게 표현되겠군.

코딩을 전체적으로 하면

#include <LiquidCrystal.h>

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 
LiquidCrystal lcd(3, 4, 9, 10, 11, 12);

void setup()
{
  lcd.begin(16,2);
  lcd.setCursor(0, 0);
  lcd.print("Hello World");
  delay(1000);
  lcd.clear();
}
void loop() {
  lcd.setCursor(0, 1);
  lcd.print("COUNT : ");
  for(int i=0;i<10;i++){ 
    lcd.setCursor(9, 1);
    lcd.print(i);    
    delay(1000);    
  }    
}

for문을 이용해서 0~9까지 i의 값이 1씩 증가하고 그 값이 커서(setCursor(9,1)로 두번째 줄의 9번째칸에 i값을 출력이 됩니다.
0~9까지 아니는 1초 단위로 그 값을 찍게되는 것이죠. 자세히 보시면 위에서 함수에 대한 요약된거랑 매칭해서 이해하세요.
대충 이런식으로 값들을 찍어낼 수 있습니다. 나중에 센서값을 측정하면 바로 LCD 16x2로 출력할 수 있겠죠.

4. 결과


동영상 결과를 회로도를 최대한 보기 편하게 배치하다 보니깐 LCD 16x2가 거꾸로 배치했네요. 그래서 글자가 거꾸로 나오는 것처럼 되었네요.

마무리


LCD 16x2 만 잘 사용하시면 이제 다양한 실험을 작은 LCD 모니터로 그 값을 출력할 수 있어서 보기 편하실꺼에요. 이걸 모니터 대용으로 아두이노상에서 모든걸 해결 할 수 있게 되겠죠. 한번 초음파센서와 결합해서 거리값을 출력시켜보세요. 다음편에 이 두 부품을 결합한 에제로 복습 실험을 할지 아니면 실제로 LCD 16x2 I2C 를 이용해서 2핀으로만 제어하는 걸 보여드릴때 고민해봐야 겠네요.
암튼 LCD 16x2를 연습을 하시고 다 소개하지 못했지만 참고자료에 링크한 곳에 가셔서 함수들을 한번 읽어보시면 표현을 해보세요.

오늘은 LiquidCrystal 에서 사용함수들만 이해만 해주세요. 그리고 예제가 어떻게 출력되는지만 잘 살펴보시면 그냥 무난히 LCD 16x2를 사용하실 수 있을꺼에요.


댓글()

[아두이노] 가변저항을 이용하여 3색 LED 제어

IOT/아두이노|2019. 2. 23. 10:17

[아두이노] 가변저항을 이용하여 3색 LED 제어



오늘은 가변저항 부품을 이용해서 3색 LED의 색상을 마음대로 조절하는 실험을 하겠습니다. 오늘의 주제는 가변저항입니다. 지금까지 실험에서는 고정저항으로 LED에 연결하여 불을 켜거나 다른 부품의 전류를 조절하기 위해서 써왔습니다. 이제는 고정저항 대신에 가변저항을 써서 유동적으로 제어할 수 있는 방법을 배워보도록 하겠습니다.

1. 가변저항


모양은 대충 이렇게 Termial1,2와 Wiper로 핀이 구성되어 있습니다. 실험에서는 Terminal2(5V), Terminal1(Gnd), Wiper(저항값추출)로 구성합니다. 가정에 보시면 형광등에 돌려서 밝기 조절하는 스위치를 보시거나 있으신분들 아마 있을거에요. 바로 그게 가변저항스위치 입니다. 가정 형관등 보시면 돌려서 저항이 작으면 전류가 통과하는 양이 많아 형광등이 밝아지고 저항이 크면 전류가 통과하는 양이 적어져서 형광등이 어두어집니다. 대충 가변저항 부품에 대해서 이해가 되시죠. 참고로 여기서 Terminal1과 Terminal2의 Vcc와 Gnd 연결 순서는 고정된 것이 아닙니다. 반대로 연결하더라도 작동은 됩니다. 자신이 연결했을때 Wiper의 값이 큰값이면은 반대로 연결하게 되면 Wiper의 값은 작은값으로 바뀌에 됩니다. Terminal1,2의 선을 어떻게 연결하느냐에 따라서 Wiper의 나오는 전류값은 서로 반대값이라고 생각하시면 됩니다. 쉽게 말해서 자신이 연결한 선에서 돌리면 0~1023으로 순차적으로 출력된다면 반대로 연결하여 돌리면 1023~0으로 순차적으로 출력됩니다. 참쉽죠.

1. 회로도 구성


  • 준비물 : 가변저항 3개, 3색 LED 1개, 저항 220옴 3개, 아두이노우노, 뻥판
  • 내용 : 가변저항스위치는 각각 RGB의 저항값을 추출하고 그 값을 토대로 RGB의 색상값으로 출력하도록 하는 실험


가변저항 부품을 보시면 Wiper에서 나오는 선이 아날로그 A0, A1, A2에 입력으로 들어옵니다. Wiper은 가변저항 부품에 흐를 전류의 값을 출력합니다. Terminal1은 Gnd, Terminal2은 5V에 연결합니다. 그러면 가변저항기에 전류가 공급되고 조절기로 돌리면 저항값이 변화되고 Wiper로 그 변화된 전류가 출력됩니다. 그 출력값이 A0, A1, A2에 아날로그 값으로 읽게 되고 그 값은 다시 3색 LED의 아날로그 신호값으로 출력되여 색이 결정됩니다. 하지만 아두이노는 아날로그 출력이 없으며 아날로그 출력과 비슷한 형태로 출력되는 PWM 출력으로 대신하는데 그냥 단순하게 PWM디지털핀으로 아날로그 신호를 내보낸다고 생각 하시면 돼요.

3. 코딩


  • 사용함수 : pinMode(), analogWrite(),analogRead()
  • 내용 : 간단히 가변저항 조절기로 조절하면 흐를 전류를 조절할 수 있고 그 조절된 전류 값을 3색 LED에 출력값으로 해서 색을 자유롭게 만들어 낸다.
  • 참고 : 3색 LED 제어(아두이노)

복습

  • pinMode(사용핀,모드) : 사용할 핀을 입력/출력모드(INPUT/OUTPUT) 인지 선언한다.
  • analogWrite(출력핀,상태) : 출력핀에 0~255 사이의 아날로그신호(PWM)를 내보낸다.(3색 LED에 출력용)
  • analogRead(입력핀) : 입력핀을 0~1023 사이의 아날로그 신호값을 읽어들인다.

설계

(1) 가변저항기 읽기

color_value[0] = analogRead(A0)/4; //(0~1023 => 1024/4=256)
color_value[1] = analogRead(A1)/4;
color_value[2] = analogRead(A2)/4;

analogRead()함수로 각 Wiper핀에서 발생된 가변저항값을 읽게 됩니다. 0~1023 사의 값을 가져오는데 3색 LED의 입력값은 0~255사이이기 때문에 나누기 4를 해줘야 되겠죠. 왜 나루기를 했는지는 아시겠죠.

color_value[0]=map(analogRead(A0),0,1023,0,255)

원래는 이렇게 코딩해야합니다. map()함수로 효율적으로 코딩해야 합니다. 하지만 대충 어떤 값이 들어가는지 직관적으로 이해 시키기 위해서 map()함수를 생략했네요.

  • map(입력값,입력최소,입력최대,출력최소,출력최대) : analogRead(A0)에서 읽은 값이 방금전 0~1023이라고 했죠. 그걸 입력최소, 입력최대로 범위를 지정해주고 이 값을 기준으로 출력값의 범위는 0~255니깐 출력최소 0과 출력최대 255로 지정하면 함수 하나로 쉽게 매핑 시킬 수 있습니다. 둘 중 아무거나 사용해서 이용하시면 돼요.

(2) 색 결정

우선 3색 LED에서 따로 Color()함수를 직접 만들었습니다. 이렇게 만들어 놓으면 3색 LED 부품을 사용하면 이 함수만 복사해오면 번거로운 코딩을 할 필요 없겠죠. 3색핀에 값이 결정되면 그 값을 color(Red, Green, Blue) 값을 넣으면 3색 LED에 색이 출력됩니다.

void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue); 
}

코딩을 하면

  1. 3색 LED에 RGB 핀에 제어할려면 3핀이 필요하니깐 우선 3핀을 변수로 선언해야지(9,10,11)
  2. 가변저항1,2,3이 있는데 이걸 저장할 변수가 필요해! 3개가 필요하니 그냥 배열변수로 하지. (color_value[3])
  3. 3색 LED에 사용할 핀이 3개니깐 변수도 선언했고 그러면 이 핀이 출력으로 사용할려면 우선 선언해야지.
  4. 자! 가변저항을 읽어와서 배열변수에다 순서대로 저장하자.
  5. 이제 3색 LED에 저장된 값인 가변저항값으로 색을 만들자
int rpin = 11;
int bpin = 10;
int gpin = 9;
int color_value[3];
 
void setup()
{
  pinMode(rpin, OUTPUT);
  pinMode(gpin, OUTPUT);
  pinMode(bpin, OUTPUT);
}

void loop()
{
  color_value[0] = analogRead(A0)/4; //(0~1023 => 1024/4=256)
  color_value[1] = analogRead(A1)/4;
  color_value[2] = analogRead(A2)/4;
  Color(color_value[0],color_value[1],color_value[2]);
}

void Color(int red, int green, int blue)
{
  analogWrite(rpin, red);
  analogWrite(gpin, green);
  analogWrite(bpin, blue); 
}

5. 결과


그냥 가변저항 예제로 LED 1개, 가변저항 1개를 이용해서 밝기를 조절하는 실험을 해도 되는데 좀 더 복습하면서 3색 LED의 색을 만들어내는게 시각적으로 보기도 좋고 실험이 재밌을 것 같아서 선택했습니다.

마무리


가변저항을 이용해서 조절하니깐 시각적으로 3색 LED의 색의 변화를 체험하셨을 거라 생각합니다. 3색 LED의 색의 변화를 통해서 뭔가 재밌는 소재가 없나 한번 떠올려 보세요. 예전에 어떤분의 블로그 글을 본적 있는데 3색 LED를 이용한 실험이였습니다. 참 참신하더군요. 비닐인가 솜인가 구름의 형태로 만들어서 그 안에 3색 LED를 넣고 wifi쉴드를 이용해서 날씨 정보를 읽어오게 했는데 그 정보를 이용해서 현재의 날씨 상태를 3색 LED의 색으로 나타냈는데 참 재밌어 보이더군요.
한번 이런건 어떨까요. 토양습도센서를 아두이노에 연결해서 식물이 물이 부족할경우 그걸 시각적으로 3색 LED로 식물의 감정을 표현한다면 괜찮겠지요.

이야기가 삼천포로 빠졌지만 가변저항을 조절할 수 있다면 과연 일상에서 어떤것들이 있는지 찾아보고 또 어디에다 이 부품을 사용하면 재미 있을지 한번 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] RC카 장애물 감지시 방향 전환 기초

IOT/아두이노|2019. 2. 22. 11:52

[아두이노] RC카 장애물 감지시 방향 전환 기초



초음파레이더의 원리는 지난 시간에 기초 실험으로 살펴 보았습니다. 하다보니깐 RC카 자율주행할 때 초음파 센서로 장애물 감지을 할 경우가 문득 떠오르더군요. 그래서 초음파레이더 만들기 소스를 그대로 이용하여 몇부분만 추가하여 코딩을 완성했습니다. 그냥 갑자기 떠오른 생각이라서 코딩은 깔끔하지 않네요. 막 떠오르는데로 코딩한거니 그 점을 감안해서 코딩을 보시기 바랍니다.

1. 회로도 구성


  • 준비물 : 서보모터 2개, 초음파센서 1개, 아두이노우노
  • 내용 :45~ 125도 서보모터를 회전시키면서 초음파센서로 거리를 측정부분은 그대로 유지한 상태에서 RC카 앞바퀴 방향 제어용 서보모터를 추가하자.


머리속에서 각 부품을 실제 RC카에 배치를 상상해 보세요. 첫번째 초음파센서가 서보모터에 부착되어 있고 두번째 서보모터는 RC카 앞바퀴에 연결되었다고 상상 해보세요.

3. 코딩


  • 사용함수 : attach(사용핀), write(각도), pulseIn(입력핀, HIGH), Serial.begin(9600), Serial.print(출력값),Serial.println(출력값)
  • 내용 : 간단히 서보모터가 회전은 45~125도 범위를 잡고 전방의 장애물 탐지를 초음파센서가 담당하게 코딩하고 앞바퀴담당 서보모터는 거리 50이하 일때 전방 90도 기준으로 좌측에 장애물 발견시 우측으로 우측 장애물 발견시 좌측으로 회전시키는 코딩을 한다.

설계

앞바퀴 방향 제어를 하기 위해서 서보모터를 제어해야겠죠.

그래서 서보모터 객체를 하나 더 선언합니다.

Servo servo2;

그리고 핀을 10번을 사용하기 위해서 연결합니다.

int servoPin2 = 10;
servo2.attach(servoPin2); //angle (0~180)

앞바퀴 장애물 방향전환 조건문을 만듭니다.(하나의 패턴으로 자율주행 첫단추로 접근하는 시작 코딩)

  if(m_distance<=50 && m_angle>90) {    
    servo2.write(45);
    delay(50);   
  }
  else if (m_distance<=50 && m_angle<90){
    servo2.write(125);
    delay(50);   
  }
  else {
    servo2.write(90);
    delay(50);   
  }
  if(m_distance<=50 && m_angle>90) {    

if조건문

  • 첫번째 조건식 : "장애물(벽) 거리가 50cm 이하이고 각도가 90도 보다 큰가?" (왼쪽장애물확인)
    그러면 왼쪽 장애물이 너무 가까우니깐 오른쪽으로 핸들을 꺽어야 겠죠.
  • 두번째 조건식 : "그게 아니면 장애물(벽) 거리가 50cm 이하이고 각도가 90도보다 작은가?"(오른쪽장애물확인)
    그러면 오른쪽 장애물이 너무 가까우니깐 왼쪽으로 핸들을 꺽어야 겠죠.
  • 거짓이면 : 그냥 앞바퀴 방향각 90도로 정중앙 정면을 향하게 한다.

즉, 조건식으로 50cm이하일때 현재 방향에 따른 핸들의 방향을 결정하게 됩니다. 50cm이하에 장애물이 없으면 그냥 정면으로 계속 진행하도록 로직을 짜봤습니다.
여기서 핸들 각도를 변수로 빼면 중복되는 코딩이 3번 반복되는데 이걸 하나로 줄일 수 있겠지만 우선은 동작을 좀 더 직관적으로 이해할 수 있도록 하기 위해서 수정은 안했습니다.

초음파레이더 소스와 위에 방향전환 로직을 합쳐서 전체코딩을 을 살펴보면

#include <Servo.h>

Servo servo1;
Servo servo2;
int servoPin1 = 9;
int servoPin2 = 10;
int m_distance=0;
int m_angle=45;
int m_chk=0;

void setup() {
  Serial.begin(9600);
  servo1.attach(servoPin1); //angle (0~180)
  servo2.attach(servoPin2); //angle (0~180)
}

void loop() {
  
  //초음파센서 몸체 회전
  servo1.write(m_angle);
  delay(50);
  
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수   
  Serial.print("d : ");
  Serial.println(m_distance);
  
  //앞바퀴 장애물 방향전환
  if(m_distance<=50 && m_angle>90) {    
    servo2.write(45);
    delay(50);   
  }
  else if (m_distance<=50 && m_angle<90){
    servo2.write(125);
    delay(50);   
  }
  else {
    servo2.write(90);
    delay(50);   
  }
  
  //초음파센서 회전각
  if(m_chk==0){
    if(m_angle<125)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>45)m_angle-=1;
    else m_chk=0;
  }
  
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

5. 결과


설계과정은 이번에 생략하고 결과만 시뮬레이터로 돌려 보는 장면만 보여 드립니다. 지난시간에 회로도 만드는 과정을 해봤으니 구지 중복해서 다시 똑같은 것을 찍을 필요는 없고 서보모터만 하나 추가했기 때문에 결과만으로 충분해서 회로도 배치과정은 생략했네요.

마무리


오늘은 아두이노 RC카에 주행에 관한 첫 단추인 방향전환에서 하나의 패턴을 기초 패턴으로 로직을 만들었습니다. 이걸로 주행이 가능하지 않습니다. 단지 이렇게 코딩해 놓고 조금씩 패턴을 늘리고 로직을 늘려가야 합니다.

여기서는 단적으로 왼쪽 장애물 감지시 레드존에 들어가고 바로 오른쪽으로 45도 회전을 시켜서 그린존으로 탈출하는 코딩입니다. 어떤 문제가 있을까요 바로 현재 RC카의 주행 속력에 대한 장애물과의 거리별로 방향전환 각도를 가변적으로 해야합니다. 그래야 매끄렇게 회전이 됩니다. 그리고 또 문제는 가령 전방 직선도로로 달릴때 통과 할 수 공간이 충분하지만 가력 왼쪽이나 오른쪽 방향에 장애물이나 벽과의 거리가 50 이하면 그냥 방향핸들을 반대 방향으로 꺽어버리는 문제가 방생합니다. 즉, 전방에 안전거리 확보가 확인되면 그상태로 주행하고 안전거리가 미확인시 좌우측에 위험거리가 감지되면 방향을 전환하더록 로직을 수정해야겠죠.

이렇게 주행에서의 문제들을 하나씩 찾아내고 그걸 해결하는 패턴들을 하나씩 만들어 내야합니다. 그래야 자율주행이 가능해지는 것이죠. 다른 방식으로 장애물 감지되면 그상태에서 주행이 정지되고 안전거리 확보 패턴로직 주행을 하고 다시 안전거리가 확보되면 주행을 다시 시작하는 방식으로도 변경할 수 있겠죠.

코딩을 쉽게 하기 위해서는 실제 장난감 자동차를 방에서 손으로 밀면서 주행을 시켜보세요 그리고 관찰하면 이런 상황일때 이걸 어떻게 어떻게 빠져나올지 손으로 자동차를 움직이시고 그 움직을 잘 기억했다가 그 움직이는 동작 패턴을 코딩화 하시면 됩니다.

암튼 집에 굴러다니는 장단감을 가지고 한번 손으로 밀면서 띠따! 빵빵! 하면서 가지고 노세요. 그리고 나서 그 움직임을 기역했다가 코딩을 상상해보세요.


댓글()

[아두이노] 초음파레이더 만들기(기본동작제어)

IOT/아두이노|2019. 2. 21. 10:16

[아두이노] 초음파레이더 만들기(기본동작제어)



이번시간에 초음파레이더를 기본동작 원리를 실험하는 시간을 갖도록 하겠습니다. 가장 많이 검색되는 주제이기도 하죠. 현재 초음파센서가 없는 관계로 실제 구동 장면은 추가하고 싶었는데 아쉽게 못 보여 드리네요. 그래도 가상시물레이터에서 충분히 표현이 되니깐 실제 만드는 것은 그리 어렵지 않을꺼에요. 물론 거리계산에 따른 오차율 보정 작업이 필요하지만요. 오늘은 서보모터를 회전시키면서 초음파센서로 거리를 측정하고 그 값을 아두이노 IDE 시리얼모니터로 거리 값이 동시에 출력되게 표현 함으로 초음파레이더의 기본 원리를 배워보도록 하죠.

1. 초음파센서 복습


제 블로그에서 정리할 때 함수로 표현했었습니다. 초음파센서로 거리를 측정하는 로직을 따로 외부함수로 만든 것을 그대로 인용해서 적용하겠습니다.int distance = duration / 57.5; 이것은 가상시뮬레이터에 대충 거리를 보정한 값이고 실제로는 공식을 해보고 거리값의 오차가 있으면 약간씩 값을 바꾸면서 거리값을 보정하시면 됩니다.

  • 공식 : ((float)(340 * duration) / 10000) / 2; (340은 초음기종에 따라 달라질 수 있는점 참고)
int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

이렇게 함수로 만들어 놓으면 나중에 초음파센서를 사용할 때 이 함수를 복사하시면 되겠죠. 이건 3핀 초음파센서여서 사실 4핀 초음파센서에서는 TRIG, ECHO핀이 따로 존재하기 때문에 setup()함수에서 각 핀을 선언해주시면 되기 때문에 CallDistance()함수에서 핀의 출력/입력모드를 선언할 필요가 없습니다. 가상시뮬레이터에서는 3핀이여서 어쩔 수 없이 추가된 것이기 때문에 실제로 1200원 짜리 4핀 초음파센서를 사용하시면 핀모드 선언을 지워주셔서 함수를 사용하시면 됩니다.
쏘는것은 TRIG, 받는 것은 ECHO 핀인 것만 기억하시면 쉽게 변경되시겠지요. 즉, 3핀 초음파센서에서 출력모드일 때 TRIG이고 입력모드일 때 ECHO라고 생각하시면 됩니다.

2. 서보모터 복습


#include <Servo.h>
 
 Servo servo;
 int servoPin = 9;
 
 servo.attach(servoPin); //angle (0~180)
 servo.write(m_angle);
 delay(50);
 
 m_angle+=1; or m_angle= m_angle+1;

9번핀을 servo모터의 출력핀으로 연결하고 servo.write()로 각도(angle)만큼 이동하는데 마지막 라인의 m_angle은 +1씩 증가합니다. 즉 0도에서 1도씩 계속 servo모터를 회전하게 되는 것이죠. 딜레이시간은 1도여서 아주 짧게 0.05초를 줬습니다.

3. 회로도 구성


  • 준비물 : 서보모터 1개, 초음파센서 1개, 아두이노우노
  • 내용 : 0~180도 서보모터를 회전시키면서 초음파센서로 거리를 측정해보자.


가장 간단한 회로도 입니다. 대충 회로도를 보시고 해당 위치의 핀에 선을 연결하시면 됩니다.

4. 코딩


  • 사용함수 : attach(사용핀), write(각도), pulseIn(입력핀, HIGH), Serial.begin(9600), Serial.print(출력값),Serial.println(출력값)
  • 내용 : 간단히 0~180의 범위를 1도씩 회전하면서 초음파로 거리를 측정하여 아두이노시리얼모니터에 거리값을 출력한다.

설계는 위의 1, 2에서 복습 차원의 코딩을 그대로 사용했습니다. 중요한 것은 0에서 180도 까지 1도씩 회전시키고 다시 180에서 0도까지 1도씩 회전하는 것이죠.

설계

(1) 제어 변수를 만든다.

Servo servo; 
int servoPin = 9;

int m_distance=0;
int m_angle=0;
int m_chk=0;
  • servo : 서보클래스 객체 선언
  • servoPin : 서보모터 제어하는 핀번호
  • m_distance : 거리 계산값(초음파센서로 거리측정동작을 수행하는 CalDistance()함수로 구한 값이 저장)
  • m_angle : 서보모터의 회전각(write()함수로 서보모터를 실제 회전)
  • m_chk : 회전 방향 상태값(초기값 0은 0에서 180도로 회전을 뜻하고 1은 180도에서 0도로 회전을 뜻한다.)

(2) m_chk 변수가 회전 방향 상태를 나타내고 제어하는 방법

if(m_chk==0){
    if(m_angle<180)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>0)m_angle-=1;
    else m_chk=0;
  }

if 조건문으로 m_chk변수의 초기값이 0이다 m_angle(각)도 0도이다. 조건식이 m_chk==0 으로 m_chk가 0과 같은가 아닌가로 물어본다. 두가지 패턴으로 분리되는 순간이다. 0가 같다면 참인문장이 1이면 거짓문장이 수행 되겠죠. m_chk변수가 초기값으로 0임으로 참이 된다. 그 안에 다시 if문으로 m_angle(각)이 180도보다 작은가가 조건식이 걸려있다. 180도보다 작으면 m_angle+=1;로 m_angle=m_angle=+1과 같은 문장이다. 즉, m_angle(각)은 1도ㅀ 증가하게 된다. 그런데 만약에 m_angle(각)가 180도 이상이 되면 else 이하 문장이 수행됩니다. m_chk=1로 회전 방향 상태값이 바뀌게 됩니다. 그러면 다음 루프때 if 조건문 m_chk가 0이 아니기 때문에 else 이하 문장이 수행되고 반대로 m_angle 변수가 0보다 크냐고 붇고 크면은 m_angle-=1로 -1도로 감소하게 된다. 위 코딩하고는 반대 동작을 수행하는 것이죠.

다시 종합해서 설명하자면 m_chk변수 기준으로 0이면 m_angle 값이 180도가 될때까지 1씩 증가하고 180도가 되면은 m_chk변수를 1로 변경하고 m_chk변수가 1이면 m_angle 값이 0도가 될때까지 -1씩 감소하게 된다. 그리고 0도가가 되면 m_chk변수를 0으로 변경한다. 이렇게 무한 반복하게 되는 로직입니다.

(3) 결과는 아두이노 IDE 시리얼모니터로 출력

Serial.begin(9600);

Serial.print("d : ");
Serial.println(m_distance);

begin(9600)은 setup()함수에 선언하고 나머지 모니터 출력은 print(출력값)와 printIn(출력값)함수가 있는데 print()은 출력값을 출력하고 새로운 라인으로 넘어가지 않고 현재 라인에 머문다. printIn()은 출력값을 출력한뒤에 새로운 라인으로 넘어가라는 의미 입니다. 문서 장성할 때 키보드 Enter 명령으로 생각하시면 됩니다. C언어에서는 '\n'로 생각하시면 됩니다.

Serial.print("d : ");
Serial.println(110);

결과 => d : 110

대충 의미를 이해하셨겠죠.

코딩을 하면

#include <Servo.h>

Servo servo; 
int servoPin = 9;
int m_distance=0;
int m_angle=0;
int m_chk=0;

void setup() {
  Serial.begin(9600);
  servo.attach(servoPin); //angle (0~180)
}

void loop() {
  servo.write(m_angle);
  delay(50);
  
  m_distance=CalDistance(7);  //초음파센서로 거리계산함수   
  Serial.print("d : ");
  Serial.println(m_distance);
    
  if(m_chk==0){
    if(m_angle<180)m_angle+=1;
    else m_chk=1;
  }
  else{
    if(m_angle>0)m_angle-=1;
    else m_chk=0;
  }
}

int CalDistance (int Pin){  //초음파센서(3핀) 예제를 그대로 외부함수로 빼냄
  pinMode(Pin,OUTPUT); //출력모드로  사용
  digitalWrite(Pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(Pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(Pin,LOW); 
  
  
  pinMode(Pin,INPUT);    //입력모드로 사용
  int duration = pulseIn(Pin, HIGH);
  
  int distance = duration / 57.5;  //가상시뮬레이션의 오차율을 줄이기 위해 이걸로 테스트 함.

  return distance; //거리값 리턴
}

5. 결과


가상시뮬레터에서 실험 결과는 깔끔하게 나왔는데 실제 표현에서는 보정 작업이 꼭 필요 합니다.

그리고, 원래 실제로 초음파레이더를 만든다면 그래픽작업으로 레이더를 만들어 레이더에 검색된 물체를 현재회전각과 거리값으로 (x,y)의 좌표를 구할 수 있고 그걸 그래픽작업한 모니터에 원하는 형태로 그래픽모양을 출력하면 좀 더 그럴싸해 지겠죠.

여기에서는 단지 아두이노 IDE 시리얼모니터에 거리값만 출력했습니다. 그게 기본 초음파레이더의 정보입니다. 서보모터의 각도와 거리만 있으면 측정된 물체의 (x,y) 좌표를 구할 수 있고 그것을 프로세싱이나 스크래치로 이미지화 하면 보기가 더 좋겠지만 핵심은 그게 아니로 회전과 거리 측정을 동시에 수행한다는 것에 있습니다. 이미지 시각화를 단지 보여주는 부수적인 효과일뿐이죠. 동영상을 보시고 서보모터가 회전하면서 시리얼모니터에 초음파 센서의 거리가 측정되어 출력되는 걸 잘 보시고 상상을 해보세요. 서보모터에 초음파센서가 부착되어 회전되는데 그 회전하는 각도에 정면의 물체와의 거리를 측정하는 모습을요. 그러면 아마 대충은 이해가 되실 꺼에요.

마무리


간단히 초음파레이더의 기본 로직만 설명했습니다. 그리고, 오늘 포스팅한 코딩은 사실 정교한 제어라고 할 수 없습니다. 왜냐면 서보모터 종류에 따라 좀 다르게 회전이 되는 경우가 있습니다. 참고로 코딩에서 servo.attach(servoPin, Min, Max)로 범위를 설정하고 servo2.writeMicroseconds(각도시간값)으로 제어하시면 더 정교하게 제어가 가능할꺼에요. 실제로 테스트 할때는 위 방식대로 해보다가 안되면 이 방식으로 바꿔서 서보모터의 각도를 제어하시면 됩니다. 4핀이였으면은 현실 4핀 서보모터랑 동일해서 더 편했을텐데 3핀 초음파센서여서 약간 코딩이 아쉽습니다.

추가로 스크래치나 프로세싱 프로그램을 다룰 수 있는 분이라면 그걸 통해서 초음파레이더에 그래픽처리를 통해 실제 레이더처럼 표현이 가능할 꺼에요. 프로세싱을 제대로 공부한적이 없고 잠깐 맛보기로 함수만 몇개 써본게 전부라 프로세싱 프로그램을 사용하여 레이더 그래픽효과를 다음편에 추가해서 보여주고 싶긴 하는데 할까 말까 고민되네요. 잠깐 프로세싱에서 레이더 표현 그래픽 함수 몇개만 공부해서 로직을 짜면 되기는 한데 프로세싱을 새로 설치하고 공부하기가 좀 귀찮아서 생략할지 아니면 표현을 할지 고민을 해 봐야 겠네요.

오늘의 포스팅의 내용은 서모모터회전을 하면서 초음파센서로 거리를 측정한다 이것만 머리속에 넣으시면 됩니다.


댓글()

[아두이노] Servo.h 없이 직접 서보모터를 회전

IOT/아두이노|2019. 2. 20. 13:38

[아두이노] Servo.h 없이 직접 서보모터를 회전



이번에 서보모터의 단순히 전원 공급으로만로 회전을 시켜보는 걸 해보겠습니다. 이것도 오래전 Servo.h 없이 제어를 할 수 있는 방법이 없을까 하고 고민을 좀 했었습니다.
https://github.com/esp8266/Arduino/blob/master/libraries/Servo/src/Servo.h 에서 Servo 클래스에 대해 분석도 하고 했지만 너무 복잡하더군요. 그냥 단순 제어를 해볼려고 조사하다가 전기 신호 파형에 따라 각도를 제어할 수 있다고 해서 digitalWrite()함수 하나로 제어하는 실험을 하게 되었습니다. 혹시 서보모터의 원리를 보다 자세히 알고 싶다면 구글링 서보모터원리라는 단어로 하시면 쉽게 찾을 수 있을거에요. 이번 실험은 전류를 서보모터에 보내고 펄스 간격을 시간값으로 해서 쪼개서 전류공급과 차단을 하면 서보모터의 회전을 시키는 실험으로 진행됩니다. 그리고 이전에 포스팅한 Ardunio min = 544, max=2400 값을 기준으로 이번에 가상시뮬테이의 회전 실험을 해보도록 하죠. 제 블로그의 있던 소스를 그대로 가져와서 실험 포스팅을 하도록 하겠습니다. 고치자니 귀찮니즘이 밀려와서 구지 고칠 필요가 없다고 생각되어 그냥 소개합니다.
그리고 가상시뮬레이터에서 실험하던 당시 서보모터가 다른 기종이여서 min=1000, max=2000을 기준으로 회전시켰지만 이번 실험은 544~2400 값으로 해서 회전이 되는 서보모터를 사용하여 기준은 544~2400으로 실험했습니다.

1. 서보모터의 회전 이해


구글링 검색하시면 서보모터 원리에 대해서 자세히 소개되어 있는데 그냥 아래의 그림처럼 단순하게 생각하시고 넘어가시면 됩니다. 전기 신호는 일정한 간격으로 해서 아래와 같은 파형을 만들어 냅니다. 전류가 공급되고 차단하는 그 시간차 값으로 서보모터의 각도를 만들어 낸다고 가단히 생각하세요. 전류 공급(5V)가 짧은 간격으로 공급된다면 회전하는 각이 그 시간만큼만 회전한다고 생각하면 됩니다.


파형을 아두이노에서 만든다면( Ardunio min = 544, max=2400 값을 기준)

   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(1500);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(펄스간격남은값);   

즉, 서보모터 핀에 HIGH(5V)의 전류를 1500마이크로초 동안 전류를 공급하고 LOW(0)로 전류를 차단을 펄스간격남은값(max-회전시간값)만큼이란 표현입니다. 현실 구동은 스피드하게 회전하지 않겠지만 가상시뮬레이터에서 저리 코딩하면 순간 90도정도의 각도로 회전해버립니다. 가상시뮬레이터에서 서보모터라이브러리에서 제공되는 함수와 비슷하게 회전을 보여줄려면은 대충 delay()함수로 시간을 좀 더 크게 대기시켜서 보면은 최대한 비슷하게 회전합니다.

대충,

void setup() {
  pinMode(9, OUTPUT);
}
void loop() {
   digitalWrite(9, HIGH);  
   delayMicroseconds(1500);     
   digitalWrite(9, LOW);   
   delayMicroseconds(2400-1500);   
   delay(100);
}

이정도만 코딩해서 아래 회로도 디자인한 곳에다 코딩을 넣어보세요. 1500시간값만큼의 각도까지 움직이다가 그 각도에서 덜덜덜 떨고 있을꺼에요. 정상적으로 동작되는걸 보실꺼에요

여기서 펄스간격남은값 2400-1500으로 해서 펄스 간격을 일정하게 만들었습니다. 그냥 아두이노의 최대값 2400을 펄스간격 기준값으로 설정했네요. 펄스간격 기준값 2400을 기준으로 얼마만큼의 시간이 5V와 0V가 되었는지의 시간입니다. 그래서 1500만큼의 전류가 공급되었으니 나머지 시간은 LOW에 할당해야겠죠.

쉽게말해서, 회전 파형을 만든다고 생각하시면 됩니다. 구지 자세히 생각하지 마시고요. 이런게 있다는 정도만 이해하시고 오늘 포스팅을 깊게 이해하실 필요 없습니다. 그냥 Servo 라이브러리 파일이 오픈소스로 제공되는데 구지 힘들게 만들 필요는 없겠죠. 이렇게 만들었다고 해서 정교한 컨트롤은 되지 않으며 단지 이런식으로 회전이 이뤄지는 거구나 정도용이니 깊게 생각하지 마세요.

2. 회로도 구성


  • 준비물 : 서보모터 1개, 아두이노우노
  • 내용 : 서보모터 회전 시키자


서보모터는 Vcc(5V), Gnd, Signal(입력신호)로 3핀이로 구성되어 있고 아두이노에서 Signal핀에 신호를 보냄으로써 각도를 제어는 지난시간과 동일합니다.

3. 코딩


  • 사용함수 : pinMode(사용핀, 모드), digitalWrite(사용핀, 상태), delayMicroseconds(시간값), delay(시간값)
  • 내용 : 간단히 180도 회전만 시키자.

설계

우선 delayMicroseconds() 함수를 사용한 목적은 서보모터를 회전시킬 시간값이고 지연시간도 이 함수로 사용했습니다. 그리고 0~180도까지 회전이 끝나면 delay()로 1초동안 잠시 대기는 목적으로 사용합니다.

그리고 digitalWrite()함수를 사용하기 위해서는 pinMode()함수로 핀모드를 설정해야 합니다. 그리고 digitalWrite()함수로 서보모터에 신호값으로 전류 공급과 차단으로 전기신호파형을 만들어 특정 각으로 회전시킵니다.

코딩을 하면

int servoPin = 9;
int m_max=2400;

void setup() {
  pinMode(servoPin, OUTPUT);
}

void loop() {
  for(int i=544;i<2400;i++){
   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(i);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(m_max-i);    
  }
  delay(1000);

  for(int i=2400;i>544;i--){
   digitalWrite(servoPin, HIGH);  
   delayMicroseconds(i);     
   digitalWrite(servoPin, LOW);   
   delayMicroseconds(m_max-i);    
  }
  delay(1000);
}
}

for문으로 사용해서 min(544)값에서 max(2400)값이 될때까지 delayMicroseconds(i)로 전류의 공급 간격입니다. 이게 바로 각도이고요. delayMicroseconds()함수을 이용하여 전기 파형을 만들어 냅니다. 전류 공급시간이 작을 경우 펄스간격 시간단위로 짧은 파형이 작게 형성됩니다. 그럴때 서보모터의 움직이는 회전이 짧아서 그 상태를 유지하는거고요 i값이 클경우 그 파형이 크게 형성됩니다. 서보모터의 움직이는 회전각 커집니다. 그리고 그 회전에 도달하면 더이상 회전을 하지 않으며 그 회전각을 계속 유지하게 됩니다. 544에서 2400될때까지 전원 공급 간격이 점차적으로 커져가니깐 결과적으로 0~180도로 회전을 시키게 되는 것이죠. 두번째 for문은 2400에서 544가 될때까지 i의 전원 공급 간격이 적어지니깐 180~0도로 회전을 시키게 됩니다.

이해가 안된다고 생각하시면 쉽게 delayMicroseconds(i) 각도로 생각하시고 전원 공급과차단의 간격을 펄스간격 기준값(m_max)값이라고 생각하시면 됩니다. 펄스간격동안 delayMicroseconds()함수로 전류의 파형을 만들고 그 파형만큼의 각도로 회전을 시킨다고만 이해하시면 됩니다.

4. 결과


따로 제작과정없이 그냥 코드만 삽입해서 그 결과를 보여드립니다. 한번 서보모터 회전를 이해하기 위해서 동영상에서 과연 1500마이크로초 동안 공급될때 어느정도의 회전이 되고 그 상태를 유지할때 서보모터의 움직임을 유심히 살펴보시면서 회전을 어떤 느낌으로 회전되는지 이해하시면 되겠습니다. 그리고 본론으로 들어간 실험 코딩은 0~180도까지 회전 시키고 1초 대기했다가 180~0도까지 회전 시키는 과정을 반복합니다. 그리고 여기서는 아두이노우노의 2400의 값을 펄스파형을 이 일정하게 증가하다가 일정하게 감소하는 과정에서 전류를 시간값으로 해서 파형을 만들어내고 그 파형에 따라서 각도가 발생하는 점을 생각하시고 동영상의 서보모터의 회전을 보시면 되겠습니다.

마무리


쉬울 것 같아서 소개했는데 하고나니깐 이거 전기 신호의 파형을 봐야하고 서보모터의 동작원리를 자세히 알아야하는데 그렇게 자세히 설명을 하자니 삼천포로 빠질 가능성이 있어서 기본만 설명하다보니 좀 애매하게 됐네요.
오늘 포스팅 내용은 그냥 이런게 있구나 정도로만 하고 그냥 넘어가시고 구지 깊게 생각할 필요 없고요 편하게 Servo.h 의 Servo 클래스를 이용하시면 됩니다.
그냥 예전에 호기심에서 시작해서 그냥 제어만 해본거라 오늘 포스팅은 가볍게 읽고 이해가 안되더라도 무시하고 넘어가시면 됩니다.


댓글()

[아두이노] 서보모터 제어

IOT/아두이노|2019. 2. 19. 09:18

[아두이노] 서보모터 제어



이전 시간까지는 칩과 관련해서 제어를 하다보니 복잡한 주제를 포스팅 했었습니다. 이번에는 좀 쉬운 주제를 다루고자 합니다. 기본 서보모터 동작 제어만 다루기 때문에 별거 아니라고 생각 할 수 있지만 서보모터 제어는 나중에 아두이노로 작품을 만들때 가장 많이 사용하는 부품입니다. 그리고 사용 목적에 따라서 코딩 능력을 필요로 하는 부분이기도 하기 때문에 사용목적에 따라 좀 어려울 질 수 있는 부품이기도 합니다. 하지만 오늘 포스팅은 단순한 제어를 통해 서보모터를 체엄하도록 하죠.

1. 서보모터



서보모터는 Vcc(5V), Gnd, Signal(입력신호)로 3핀으로 구성되어 있습니다. 아두이노에서 Signal핀에 신호를 보냄으로써 각도를 제어하게 됩니다.

2. 회로도 구성


  • 준비물 : 서보모터 1개, 아두이노우노
  • 내용 : 서보모터 회전 시키자


가장 간단한 회로도 입니다. Vcc은 5V에 Gnd은 Gnd에 Signal은 9핀에 연결하면 완료입니다. 원래 모터제어를 할때는 모터쉴드보드가 필요합니다. 하지만 서보모터의 경우는 아두이노에서 직접 제어해도 됩니다.

3. 코딩


  • 사용함수 : attach(사용핀), write(각도), writeMicroseconds(각도시간값)
  • 내용 : 간단히 180도 회전만 시키자.

설계

(1) 서보모터객체 선언

서보모터를 사용하기 위해서는 제어함수들이 있는 Servo클래스를 객체로 선언합니다. 이말은 예전에 변수 선언과 같은 의미로 생각하시면 됩니다. Servo 클래스를 하나의 변수에 저장되었다고 생각하시면 됩니다.

#include <Servo.h> 
Servo servo1; 

Servo.h 파일에 있는 Servo 클래스를 servo1이라는 객체변수로 선언합니다. 그래서 servo1 객체를 통해서 Servo 클래스 안에 함수들을 사용하게 됩니다. 쉽게말해서 servo1.함수() 이렇게 Servo클래스의 함수를 사용할 수 있게 됩니다.

(2) 서보모터객체 연결

servo1.attach(사용핀);
servo1.attach(사용핀,min,max); //Ardunio min = 544, max=2400

attach()함수는 해당핀을 서보모터를 제어하는 핀으로 사용하겠다는 의미로 받아 들이시면 됩니다. 여기서, 두가지 방식이 있는데 첫번째, 사용핀만 인자로 넘겨주면 기본값으로 자동으로 세팅됩니다. 두번째, min, max로 범위를 따로 지정해 줄 수 있습니다. 두번째 방식이 좀 더 서보모터를 정교하게 제어할 수 있습니다.

과거 tinkercad.com 사이트로 옮기전 사이트에서는 서보모터가 다른 종류가 한개 더 있었는데 그 모터에서는 min, max값을 1000, 2000으로 줘서 1500일때 90도 회전이였습니다. 가상시뮬에터에서는 그냥 544, 2400으로 맞춰서 실험했네요. 서보모터마다 제어하는 각도가 차이가 있기 때문에 조정 작업이 필요합니다. 그래서 범위를 지정해서 하실때는 사용하시는 서보모터의 종류에 따라 다르니깐 꼭 보정을 하셔야 합니다.

(3) 서보모터객체 제어

servo1.write(180); 
servo1.writeMicroseconds(2400);

여기에서 write(각도)로 제어하시면 서보모터 기종에 따라서 약간 차이를 보이게 됩니다. 정확하게 제어가 안될 수 있습니다. 하지만 writeMicroseconds(2400)로 제어하시면 오차율을 줄일 수 있습니다. 하지만 보정을 해야하기 때문에 좀 귀찮은 점이 있지요.

(3) delay()함수 사용

delay(2000);

180도를 회전 시킬려고 하는데 일정 시간이 필요합니다.

만약에,

servo1.write(180); 
delay(500);
servo1.write(0); 
delay(500);

delay(500)으로 할 경우 0.5초동안 180도로 갈려고 회전하는 도중에 딜레이 시간이 끝나면 0도로 가는 명령라인이 실행됩니다. 그러면 180도 회전이 안된 상태에서 다시 0도로 회전되어 버리는 현상이 발생합니다. 그래서 각도별 delay()함수로 시간 조정이 필요합니다.

코딩을 하면

첫번째,

#include <Servo.h> 

Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin1); 
}

void loop() 
{
    servo1.write(180);     
    delay(2000);
    servo1.write(0);     
        delay(2000);
}

두번째,

#include <Servo.h> 
 
Servo servo1; 
int servoPin1 = 9;

void setup() 
{ 
    servo1.attach(servoPin2,544,2400); //Ardunio min = 544, max=2400
}


void loop() 
{
    servo1.writeMicroseconds(2400);
    delay(2000);
    servo1.writeMicroseconds(544);
    delay(2000);   
}

5. 결과




가장 빠르게 작업했네요.

마무리


그러면 서보모터로 제어할 수 있는게 뭐가 있을까요. 지금까지 다룬 부품중에 이거랑 같이 쓸 수 있는게 뭐가 있을지 곰곰히 생각해 보세요.

가령 초음파센서를 여기에다 붙이여 어떻게 될까요. 서보모터로 각도가 제어된다면 거기에 초음파센서가 부착된다면 초음파 센서를 회전 시킬 수 있다는 의미가 됩니다. 회전 각도에 따른 거리측정이 이루어지면 어떤 일이 할 수 있을가요. 가장 많이 알려진게 레이더 입니다. 즉, 각도별 거리를 측정해서 모니터에 초음파센서에 감지된 장애물을 표시 할 수 있습니다. 또 하나는 자율주행차 입니다. 주변의 장애물을 측정해서 미로 같은 곳을 주행하면서 혼자서 피해다니면서 주행을 할 수 있게 되겠죠. 또 한가지는 180도 각도의 거리를 측정하여 그 거리들을 일정한 간격으로 게속 누적 측정한다면 그 데이터로 3D Rendering도 수행이 가능합니다. 그외로 서보모터와 초음파센서가 결합하면 다양한 표현이 가능합니다.

그러면 서보모터만으로 제어할 수 있는 다른 것들은 뭐가 있을까요. 이 글을 읽으시는 당신의 가장 가까운 곳에 정답이 있습니다. 바로 당신이 하는 행동을 비슷하게 제어할 수 있습니다. "뭘까요!" 그것은 바로 관절입니다. 관절 꺽기가 된다는 것이죠. 당신의 손을 바라보세요. 그리고 둘째(집게) 손가락을 한번 구브렸다 펴보세요. 둘째 손가락은 3마디이고 그 중간 관절을 중심으로 각 마디가 구브러졌다 펴질 꺼에요. 바로 그게 회전입니다. 그 관절 부위를 서보모터로 제어한다고 생각하시면 됩니다. 손가락 하나를 제어하게 되면 손을 제어할 수 있게 됩니다. 그러면 한쪽 팔을 제어할 수 있게 되고 그러면 로보팔을 만드는데 이용하면 되겠죠. 보다 정교하게 제어하고 싶을때 360도 회전하면서 하고 싶다면 스템모터로 제어하면 됩니다.

기본 제어로 180도 회전이였지만 좀 더 전문적으로 제어하기 위해서는 현실에서 곤충의 보행을 관찰하면서 관절의 각도를 계산해서 4,6,8축 로못으로 보행을 시킬 수 있고 사람의 손 동작의 움직일때 각 마디의 각도를 관찰하여 각도를 계산하시면 로봇팔을 만들 수 있습니다.

마지막으로 본인의 움직임의 각도를 측정해서 그걸 아두이노로 서보모터로 회전시켜 비슷하게 표현 하는 것을 상상해 보세요. 참고로 여러개의 서보모터를 아두이노에서 제어가 가능하지만 서보모터의 전류를 아두이노에서 전부 공급하면 안됩니다. 가상시뮬레이터에서는 가능하지만 현실에서는 모터에게 전류를 순간 많이 공급하면 아두이노에 불안정 전류 공급이 이루어질 수 있기 때문에 안좋습니다. 외부전류 공급을 추천 드려며 많은 모터를 제어하고자 할때는 따로 여러모터를 한번에 제어할 수 있는 보드가 있는데 오래전에 본거라 보드 이름이 생각 안나네요. 한번 구글링 하셔서 여러 모터를 제어할 수 있는 보드를 찾아보세요.


댓글()

[아두이노] 74HC595+CD4511+7SegmentLED 제어

IOT/아두이노|2019. 2. 18. 08:19

[아두이노] 74HC595+CD4511+7SegmentLED 제어



최종적으로 쉬프트레지스터(74HC595)와 7 segment Decoder(CD4511)를 결합하여 7 Segment LED를 제어하는 실험을 하고자 합니다. 아두이노에서 7 Segment LED를 제어하는 실험에서 출발하여 74HC595, CD4511 칩들을 개별적으로 이용하여 7 Segment LED를 제어 했었습니다. 이 두 칩을 연결하여 제어를 통해 7 Segment LED 실험을 마루리 하고자 합니다.

1. 7 74HC595 + CD4511 연결



위 그림에서 보면 아두이노에서 74HC595 칩으로 3개의 핀으로 입력을 하면 74HC595 칩에서는 8개의 출력 신호를 만들어 냅니다. 8개의 출력 신호에서 4개의 출력로 나눠서 각 CD4511 칩에 입력으로 들어가고 CD4511 칩에서 7개의 출력 신호를 최종적으로 만들어 냅니다.

여기서 출력핀을 제외한 나머지 핀들은 Vcc, Gnd에 연결되는 핀인데 그림에서는 생략 했습니다. 혹시 이해가 안되시는 분들은 위의 참고자료를 보시고 각 칩에 대한 연결을 다시 공부하셔야 합니다.

2. 회로도 구성


  • 준비물 : 74HC595 칩 1개, CD4511 칩 1개, 7 Segment LED 2개, 저항 330옴 2개, 저항 10k옴 8개, 아두이노우노, 뻥판
  • 내용 : 74HC595+CD4511 칩을 연결하여 7 Segment LED에 숫자를 출력하게 하자. 참고로 7 Segment LED(캐소드형)


전체 회로도 구성을 보시면 머리가 복잡해질 꺼에요. 이것은 개별적으로 이해 하시면 쉬울 꺼에요.

첫번째, 74HC595 칩을 제어하기 위해서 9,11,12핀을 이용한다. 회로도 74HC595 칩에 입력선과 Vcc, Gnd 선 연결만 생각하시고 출력선은 잠시 잊어주세요.

두번쨰, CD4511 칩을 제어하기 위해서는 4개의 입력이 필요합니다 하지만 회로도에서 입력선을 제외한 나머지 Vcc, Gnd 선 연결만 생각하시고 입력선, 출력선은 잠시 잊어 주세요.

세번째, CD4511 칩 입력 4개의 핀이고 8421(BCD) 코드표의 기준으로 배치하니깐 '0000'은 74HC595 출력핀 기준으로 '4321' 핀 순서로 입력을 넣어야 합니다. 그래서 1=A, 2=B, 3=C, 4=D 핀끼리 연결합니다. 나머지 CD4511 칩에다도 '0000'은 '8765' 핀 순서로 입력을 넣어야 겠죠. 5=A, 6=B, 7=C, 8=D 핀끼리 연결합니다.

네번째, 7 Segment LED은 CD4511칩의 출력 A,B,C,D,E,F,G핀에서 7 Segment LED의 입력 a,b,c,d,e,f,g핀으로 순서대로 A=a, B=b, C=c, D=d, E=e, F=f, G=g로 연결하면 됩니다.

다섯번째, 7 Segment LED은 캐소드형으로 변경 해주세요. 보고 똑같이 선 연결했는데 왜! 안돼! 이럴 수 있습니다. 애노드형인지 캐소드 형인지 꼭 확인이 필요합니다. 7 Segment LED은 캐소드형이여서 COM2의 핀에 Gnd로 연결을 하게 됩니다. 딱 볼때 COM1 or COM2가 Vcc로 연결되면 애노드형, Gnd로 연결되면 캐소드형으로 생각하시면 됩니다.

보시면 선연결이 많다 보니깐 복잡해 보이는데 개별적으로 하나씩 선 연결하시면 그리 어렵지 않을거라 생각 되네요.

3. 코딩


  • 사용함수 : pinMode(출력핀, 모드), digitalWrite(결쇠핀, 열림/잠금), shiftOut(데이터입력핀, 클럭핀, 순서, 데이터전송값)
  • 내용 : 쉬프트레지스터(74HC595)에 맞춰서 2개의 7 Segment LED에 숫자를 출력해 보자.
  • 참고 : 쉬프트레지스터(74HC595) 제어(아두이노)

설계

(1) 숫자패턴 배열변수 만든다.

byte data[]={
0B00000000,
0B00000001,  
0B00000010,
0B00000011,
0B00000100,  
0B00000101,  
0B00000110,  
0B00000111,
0B00001000,  
0B00001001,  
};

지난 시간의 쉬프트레지스터(74HC595) 코딩을 기준으로 약간만 수정하여 의미만 전달하도록 코딩했습니다. 8개의 비트값이 있습니다. 왼쪽 4개는 왼쪽 CD4511 칩에 입력값이고 오른쪽 4개는 오른쪽 CD4511 칩에 입력값이 됩니다.

즉,

0000 0001 => '0' 과 '1' 이 7 Segment LED에 출력됩니다.

만약에 '1'과 '2'을 출력하고 싶다면 바이트 값이 어떻게 바꿔야 할까요.

0001 0010 => 0B00010010

이해가 되셨죠.

(2) 숫자 출력

digitalWrite(latchPin, LOW); //열림
shiftOut(dataPin, clockPin, MSBFIRST, data[i]); //패턴입력
digitalWrite(latchPin, HIGH); //닫힘

코딩을 하면

byte data[]={
0B00000000,
0B00000001,  
0B00000010,
0B00000011,
0B00000100,  
0B00000101,  
0B00000110,  
0B00000111,
0B00001000,  
0B00001001,  
};

int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop(){
 for(int i=0;i<10;i++){
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    digitalWrite(latchPin, HIGH);       
    delay(1000);
  }  
}

4. 추가 코딩(프로그램영역이라 생략하셔도 됨)


본 코딩은 제 블로그에 올렸던 코딩으로 설명 드리겠습니다.

0B00000000을 16진수로 표현할 수 있다고 했죠. 16진수로 '0x00'로 표현이 됩니다.
그러면 위 값들을 다시 표현을 하면은 다음과 같습니다.

0x00
0x01
0x02
0x03
0x04
0x05
0x06
0x07
0x08
0x09

자세히 보시면 한자리씩 증가되는걸 보실 수 있을꺼에요. 그러면 11이면'0x11'로 표현이 되면 '0x' 이것만 빼면은 그냥 숫자로 생각해도 되겠다는 생각이 아마 들꺼에요. 이걸 이용해서 숫자를 쉽게 만들어보자는 생각을 하셔야 합니다.

7 Segment LED가 2개를 사용하니깐 16진수를 사용하면 2자리 숫자를 쉽게 표현이 되겠죠.

byte total=0x00; //출력
byte A=0x00; //1의 자리
byte B=0x00; //10의 자리

이렇게 3개의 변수를 선언합니다. loop()함수 내에서

A=A+0x01;  or A+=0x01;

이렇게 하면 A은 0x01씩 증가합니다. delay(1000) 함수로 1초 단위로 저 명령문을 수행하면 1초 단위로 '0x01'씩 숫자를 증가 시키고 그 값은7 Segment LED로 숫자 1씩 증가한 숫자가 출력되겠죠.

1분짜리 시간을 카운터 한다고 해봅시다.

void loop(){
  
  total = A+B;  
  현재 total값 74HC595에 출력;

  delay(1000);
    
  A=A+0x01; 
  if(A==0x0A){
    A=0x00;
    B=B+0x10;
  }
    
  if(total==0X59) B=0x00;
}

A을 '0x01'증가 시킵니다. if 조건문으로 '0x0A' 이면 '0x00' 초기화 합니다. 여기서 '0x0A'은 10을 나타냅니다. 16진수는 9까지는 숫자로 나타내고 10부터 A,B,C,D,E,F로 0~15까지 숫자를 한자리에 표현을 할 수 있습니다. 그래서 16진수인거죠. 우리가 일상에서 쓰는 0~9까지 10진수 입니다. 그렇다고 10진수라고 부르며 쓰지는 않죠. 생략해서 그냥 숫자를 쓰는 것 뿐이죠. 16진수는 0~15까지 한자리에 표현할 수 있는 숫자라는 것만 머리속에 담아 놓으시면 됩니다.

다시 돌아와서 A은 일의 자리이기 때문에 0~9까지 숫자가 표현되고 10(0x0A)가 되면 안되겠죠. 딱 9(0x09)까지만 출력되여야 하기 때문에 조건문으로 10인지 체크하는 것이죠. 10이면 B가 10의 자리이기 때문에 그때 두번째 자리에 '0x10'을 더하게 됩니다. 조건문에서 10이 되니깐 1의 자리는 '0x00'으로 초기화 하고 10의 자리는 '0x10'으로 1이 증가 시키는 로직으로 표현된 것이죠.

마지막으로 if문은 total이 0x59이면 이전에 delay()함수로 1초가 지났기 때문에 실제는 60초가 됩니다. 하지만 loop()함수가 다음 호출되어 반복하기 전이기 때문에 total값은 59초값을 가지고 있기 때문에 if 조건문에서 '0x59' 인가라고 조건을 단것이지요. 실제로는 60초인가라는 조건문으로 생각하시면 됩니다. 60초면 B를 초기화 시키기 위해서 B=0x00로 표현하게 됩니다.

결론은 첫번째 if문은 A을 증가시킨값이 10초 되면 A를 초기화 후 B를 1씩 증가시키고 두번재 if문 B가 60초가 되면 B를 초기화 시키라는 로직입니다. 60초 되면 A은 10초가 되고 A는 초기화 되고 B은 60초 되면 당연이 B도 초기화 됩니다. 다음 루프함수는 total = 0x00 + 0x00; 이 됩니다. 결론은 00이 7 Segment LED에 출력이 됩니다.

이걸 이용하시면 시계를 만들 수 있겠죠. 초기값을 현재 시간값으로 저장시키면 자동으로 그 시간을 기준으로 시간을 계속 출력시킬 수 있겠죠.

코딩을 수정을 하면

byte total=0x00;//출력
byte A=0x00; //1의 자리
byte B=0x00; //10의 자리

int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}

void loop(){

  total = A+B;  
   
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, total);
  digitalWrite(latchPin, HIGH);       
  
  delay(1000);
    
  A=A+0x01; 
  if(A==0x0A){
    A=0x00;
    B=B+0x10;
  }
    
  if(total==0X59) B=0x00;
}

5. 결과


회로도를 만드는 과정을 영상으로 만들려고 하니깐 너무 심한 노가다여서 그냥 마지막 출력된 영상만 보여줄까도 고민하다고 그래도 전과정을 보여주는게 나을 것 같아서 이미 회로도를 만들어 놓은게 있었지만 한번 더 작업을 했네요.


마무리


드디어 7 Segment LED를 아두이노 제어에서 여기까지 왔습니다. 다른 칩까지 가면 너무 힘들 것 같아서 여기서 최종적으로 마무리 할까 합니다. 원래는 74HC595, CD4511 칩들은 대량의 LED를 제어할 목적으로 많이 쓰는 것 같더군요. 이 칩만 잘 활용해도 아두이노우노의 적은 핀을 가지고도 많은 부품들을 제어할 수 있어 확장성이 엄청 좋습니다. 한번 힘들더라도 부품의 데이터시트나 여러 블로그의 예제들을 보고 연구해 보셨으면 합니다.
이것만 알아도 표현할 수 있는 것들이 무궁무진 하니깐 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 스위치버턴으로 CD4511를 제어해 보자

IOT/아두이노|2019. 2. 17. 17:31

[아두이노] 스위치버턴으로 CD4511를 제어해 보자



지금까지 7 Segment LED를 제어하는데 필수적으로 아두이노를 사용했습니다. 하지만 이번에 아두이노 없이 4x스위치버턴으로 CD4511 칩의 입력값을 제어하여 그 결과를 7 Segment LED로 출력하는 실험입니다. 보다 쉽게 CD4511 칩 제어하는 원리를 이해하실 수 있을거에요.

1. 회로도 구성


  • 준비물 : CD4511 칩 1개, 7 Segment LED 1개, 저항 330옴 1개, 저항 10k옴 4개, 파워서플라이. DIP Switch SPST x 4
  • 내용 : CD4511 칩을 이용해서 7 Segment LED에 숫자를 출력하게 하자. 참고로 7 Segment LED(캐소드형)

지난 시간의 회로도에서 아두이노를 뺀 부분에 파워서플라이. DIP Switch SPST x 4를 연결한 회로도입니다.


2. 스위치버턴와 CD4511 칩의 동작 원리


스위치 버턴(DIP Switch SPST x 4)의 동작 원리는 지난 시간의 아두이노코딩에서 패턴을 생각하시면 됩니다.

int data[10][4]={
  {0,0,0,0}, //0
  {0,0,0,1}, //1
  {0,0,1,0}, //2
  {0,0,1,1}, //3
  {0,1,0,0}, //4
  {0,1,0,1}, //5
  {0,1,1,0}, //6
  {0,1,1,1}, //7
  {1,0,0,0}, //8
  {1,0,0,1}  //9
};

이렇게 2차배열변수로 패턴을 저장했었죠. 이 값은 8421(BCD) 코드값입니다.

0000 //0
0001 //1
0010 //2
0011 //3
0100 //4
0101 //5
0110 //6
0111 //7
1000 //8
1001 //9
....
1111 //F

이렇게 8421(BCD) 코드에서 0~9까지만 사용 합니다. 그리고 자세히 보시면 네자리로 구성되어 있는걸 보실거에요. 회로도에 보시면 4x스위치 버턴을 구성되어 있습니다. 여기서 눈치가 빠른분들은 아 8421 코드표랑 4x스위치버턴과 연관있지 않을까 생각하실 겁니다.
회로도의 스위치 버턴을 자세히 보시면 8421 코드표랑 4x스위치버턴을 일치시켰습니다. 그리고 4x스위치버턴의 방향이 전부 아래 방향으로 향하고 있는데 이상태는 전류가 차단 된 0V의 상태이고 버턴이 위로 향할때 CD4511 칩에 Vcc의 전류가 흘러 들어가 됩니다.

4x스위치 버턴의 위치에 따라 동작은 -

  • 0000 상태 : CD4511(7 Segment Decoder) 칩의 출력은 7 Segment LED의 0의 LED가 불이 들어오도록 출력
  • 0001 상태 : CD4511(7 Segment Decoder) 칩의 출력은 7 Segment LED의 1의 LED가 불이 들어오도록 출력

설명보다도 실제 가상시뮬레이터로 4x스위치 버턴을 한번 위아래로 8421 코그 값대로 1이면 위로 올리고 0이면 아래로 내리면서 스위치 버턴을 조작하시면 CD4511 칩의 입력신호에 따른 7 Segment LED로 어떻게 출력되는지 쉽게 확인할 수 있을꺼에요.

좀 더 CD4511(7 Segment Decoder) 칩의 출력 상태를 알고 싶다면 7 Segment LED 대신에 7개의 LED를 배치하여 정확히 CD4511(7 Segment Decoder) 칩의 7개의 출력 값들의 변화를 확인할 수 있습니다.

예)

0은 0000 => 출력값은 1111110 됩니다.

3. 결과


가상시뮬레이터에 회로도를 배치하는 과정과 결과가 어떻게 출력되는지 보여주는 영상입니다. 잘 모르셔도 그대로 따라서 회로도를 만들어 보시면 글을 읽는것보다 싶게 동작 원리가 이해가 되실 꺼에요.


마무리


여기서, 4x스위치 버턴을 통해 CD4511 칩을 제어하는 회로도를 소개하는 이유는 CD4511 칩의 입력신호에 대한 출력신호를 쉽게 이해하기 위해서 입니다. 그리고, 4x스위치 버턴을 통한 실제 수동 제어를 해보면서 이런 동작을 지난시간에 아두이노 코딩제어를 통해 7 Segment LED 숫자를 만든 것을 직접 수동제어를 통해 느끼게 하는게 목적이기도 합니다. 또한, 지난시간에 명령 로직 코딩을 통해 쉽게 0~9까지 출력했던 아두이노와 비교를 통해 현실의 동작을 프로그램화 하면 얼마나 편하게 제어가 되는지 보여줄려는 의도도 있지요.


댓글()

[아두이노] 7 Segment Decoder(CD4511) 제어

IOT/아두이노|2019. 2. 16. 14:57

[아두이노] 7 Segment Decoder(CD4511) 제어



이번에도 새로운 칩을 소개 합니다. 7-segment Decoder(CD4511)은 7 Segment LED를 제어하는데 효율적인 칩입니다. 다른곳에서 원리만 이해하셨다면 사용 가능합니다. 이 칩은 4가지 값에 의해서 7개의 출력값을 만들어 낼 수 있습니다. 이 칩도 제 블로그에서 소개했던 내용인데 7 Segment LED를 포스팅 할때 한번에 소개하는게 나을 것 같아서 가상시뮬레이터에서도 제공되는 칩으로 이번에는 CD4511 칩을 이용해 실험하고자 합니다.

1. 7 segment Decoder(CD4511)



위 그림에서 보는것 처럼 입력 A,B,C,D 핀으로 아두이노에서 4개의 핀의 출력값을 CD4511 칩에 입력으로 받게 됩니다. CD4511 칩의 연결은 다음 과 같습니다.

  • Vcc핀 과 Gnd핀은 아두이노에 5V와 Gnd 핀에 연결
  • LT핀와 BI핀은 Vcc로 연결
  • LE핀은 Gnd로 연결
  • 입력 A,B,C,D핀은 아두이노 디지털핀 4개 연결(실험에서는 2,3,4,5핀 사용)
  • 출력 a,b,c,d,e,f,g핀은 7 Segment LED의 DP핀을 제외한 나머지 순차적으로 A,B,C,D,E,F,G핀에 연결

여기서 회로도 설계할때에 선 연결이 노가다 작업으로 좀 힘들지만 그래도 한번 이해하면 나중에 쉽게 사용이 가능해 집니다. CD4511 칩에 선들을 어떻게 연결하는지가 숙지하시면 됩니다.

2. 회로도 구성


  • 준비물 : CD4511 칩 1개, 7 Segment LED 1개(캐소드형임), 저항 330옴 1개, 저항 10k옴 4개, 아두이노우노, 뻥판
  • 내용 : CD4511 칩을 이용해서 7 Segment LED에 숫자를 출력하게 하자. 참고로 7 Segment LED(캐소드형)


CD4511 칩에 입력을 바로 아두이노의 출력값 5V를 보내지 않고 10K옴의 저항을 붙여줍니다. 스위치 버턴을 연상하시면 됩니다. 어짜피 상태값만 보내주면 되기 때문인거죠. 정확히 CD4511 칩의 허용 전압이 기억이 안나서 안전하게 저항을 붙였습니다. 데이터시트를 인터넷에서 찾아보셔서 허용 전압을 참고하시면 되겠습니다. 위에서 설명한 CD4511 칩의 핀들이 어디에 연결하는지 설명한대로 실제 선을 연결하시면 됩니다. 그리고 주의할 것은 7 Segment LED은 캐소드형 입니다.

처음에만 복잡해 보일뿐 익숙해지면 단순합니다. 핀에 마우스를 대면 핀 이름이 나오고 해당된 부품과 연결하시면 됩니다.

3. 코딩


  • 사용함수 : pinMode(출력핀, 모드), digitalWrite(출력핀, 상태값)
  • 내용 : 아두이노에서 4개의 출력 상태를 값을 CD4511 칩을 제어하여 7 Segment LED에 숫자를 0~9까지 순차적으로 출력해보자.(7 Segment LED은 캐소드형으로 이 기준으로 코딩함)

  • CD4511 제어 :
    • digitalWrite(출력핀, 상태값)으로 CD4511 칩의 출력값을 결정

참쉽죠. LED 제어할대 배운 PinMode()와 digitalWrite()함수만 사용하시면 됩니다.

설계

(1) 숫자 패턴만들기

CD4511 4개의 입력값을 받습니다. 그러면 4개의 상태를 8421 BCD 코드표를 참조하시면 좋습니다. 캐소드형 7 Segment LED를 하면 정확히 아두이노 핀의 역순으로 코딩하면 일치하게 됩니다. 그래서 이번 실험은 이해하기 쉽게 캐소드형 코딩을 선택했습니다.

0000 => 0
0001 => 1
0010 => 2
...

0은 0000 => 출력값은 1111110 됩니다.
0은 A[4] = {0,0,0,0}
그러면 0~9까지 10개의 패턴이니깐 2차배열변수로 만들면은 아래와 같이 만들어 지겠죠.

int data[10][4]={
   {0,0,0,0}, //0
   {0,0,0,1}, //1
   {0,0,1,0}, //2
   {0,0,1,1}, //3
   {0,1,0,0}, //4
   {0,1,0,1}, //5
   {0,1,1,0}, //6
   {0,1,1,1}, //7
   {1,0,0,0}, //8
   {1,0,0,1}  //9
};

(2) 숫자 출력

0은 A[4] = {0,0,0,0} 이고,
datapin은 datatpin[4] = { 5,4,3,2} 하면

 for(int j=0;j<4;j++){
    digitalWrite(datapin[j],A[j]);
 }

이렇게 됩니다. 하지만 10개의 패턴과 각 패턴의 상태값 4개를 2차 배열 위의 date[10][4]로 만들면 2차 for문을 사용하면

  for(int i=0;i<10;i++){
     for(int j=0;j<4;j++){
       digitalWrite(datapin[j],data[i][j]);
     }
     delay(1000);
  }     

여기서 delay(1000)을 두어 1초 단위로 0~9까지 7 Segment LED에 출력되게 됩니다.

코딩을 하면

int data[10][4]={
   {0,0,0,0}, //0
   {0,0,0,1}, //1
   {0,0,1,0}, //2
   {0,0,1,1}, //3
   {0,1,0,0}, //4
   {0,1,0,1}, //5
   {0,1,1,0}, //6
   {0,1,1,1}, //7
   {1,0,0,0}, //8
   {1,0,0,1}  //9
};
int datapin[4] ={5,4,3,2};

void setup(){
  for(int i=0;i<4;i++){
    pinMode(datapin[i],OUTPUT);
  }
}
void loop(){
   for(int i=0;i<10;i++){
     for(int j=0;j<4;j++){
       digitalWrite(datapin[j],data[i][j]);
     }
     delay(1000);
  } 
} 

4. 결과


결과 동영상을 보면 다이렉트로 전 과정을 동영상 촬영을 했는데 회로도를 만드는 과정에서 Vcc 선을 Gnd로 잘못 연결해서 가상시뮬레이터 처음 실행에서 CD4511 칩이 망가졌습니다. 가상시뮬레이터라서 실제로 CD4511 칩이 망가지는 것이 아니기 때문에 그렇게 큰 의미는 없지만 이걸 실제로 선을 잘못 연결한다면 칩에 문제가 생기겠지요. 다시 동영상으로 촬영할려다가 선 연결의 중요성과 이런 점에서 가상시뮬레이터를 사용의 장점이 될 것 같아서 그냥 올립니다.(실제로 다시 촬영하기 귀찮음!)

마무리


74HC595 칩은 3핀을 사용했지만 CD4511 칩은 4핀을 사용합니다. 그러면 이전 시간에 배운 74HC595 칩으로 하는게 좋을 것 같다는 생각을 하실 수 있습니다. CD4511 칩을 제어는 무지 간단 합니다. 아두이노 없이도 이 칩을 이용해서 7 Segment LED를 제어가 가능합니다.

그리고 74HC595+CD4511으로 7 Segment LED를 제어를 한번 해보세요. 이 두 칩을 배우고 나서 이걸 합쳐서 실험해보고 싶어져서 7 Segment LED를 제어 해보았어요. 이 내용은 다음 포스팅에 쓰도록 할께요. 한번 두 칩에 동작 원리를 생각하고 회로도를 구성하고 코딩해보세요.

한번 74HC595칩과 CD4511을 결합을 하면 어떻게 회로도가 표현 되는지 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 74HC595+ 7 Segment LED 실제 실험

IOT/아두이노|2019. 2. 15. 09:26

[아두이노] 74HC595+ 7 Segment LED 실제 실험



가상시뮬레이터에서 아두이노우노를 다루면서 실제 현실에서도 동일하게 작동을 하는지 궁금하신 분들이 있을꺼에요. 그래서 귀찮니즘을 안고 실제로 실험을 하였습니다. 예전에 74HC595 칩을 뻥판에 꼽을 개고생 했는데 뻥판이 싼거라 핀이 잘 안들어가서 핀 부분이 약간 오그라졌는데 이번에도 역시나 꼽을 때 오그라지고 뽑을 때 오그라져서 나오더군요. 몇번 실험하고 나면 74HC595 칩이 망가질것 같네요.

1. 회로도 구성



[아두이노] 쉬프트레지스터(74HC595) 제어 편에서 실제로 7 Segment LED도 뻥판에 배치를 해야하기 때문에 약간 배치하는 위치를 수정했네요.


좀 더 깔끔해진 회로도 모습이지요.

지난시간에 가상시뮬레이터로 설계한 모습에서 다시 가상시뮬레이터의 뻥판에 재배치한 모습입니다. 그런데 실제로 같은 위치에 배치하면 이런 모습이 아니라 지져분한 모습입니다. 가상시뮬레이터에서 회로도를 배치한대로 실제로 동일하게 배치했습니다. 참고로 저항은 220옴뿐이 없어서 이걸로 했습니다. 저항은 LED에 필요한 허용치 저항을 연결하면 되니깐 제 경우는 저항 220옴으로 실험했네요.


위 사진은 뻥판 연결된 모습인데 실험 중의 한 컷인데요. 선만 봐도 엄청 지져분하죠. 74HC595 칩을 뻥판에 꼽을 때 개고생 했네요. 핀 위치는 대충 가상시뮬레이터에서 연습 몇번해서 그런지 선 연결은 지져분할 뿐 간단했네요. 가상시뮬레이터에 연습없이 바로 실제로 실험한다면 선 연결할 때 실수하는 경우가 많겠죠.

2. 코딩


코딩은 지난시간에 숫자를 순차적으로 출력하는 로직을 그대로 집어 넣었습니다.

byte data[]={
0B10000001,  
0B11110010,
0B01001001,
0B01100000,
0B00110011,
0B00100100,
0B00000101,
0B11110000,
0B00000001,
0B00100000
};
int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop(){
 for(int i=0;i<10;i++){
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    digitalWrite(latchPin, HIGH);       
    delay(1000);
  }  
}

3. RaspberryPi3 에서 아두이노우노로 프로그램 이식


PC에서 RaspberryPi3를 원격 제어를 통해 아두이노 IDE를 띄워고 위 코딩을 복사한 뒤에 실제 아두이노우노에 프로그램 이식을 수행했습니다.
PC에 아두이노 IDE를 설치했다면 바로 PC에서 아두이노우노에 프로그램을 이식하면 됩니다.


4. 결과


아래 13초짜리 동영상은 실제 프로그램이 이식된 아두이노우노가 74HC595 칩에 숫자 패턴 데이터값을 전송하여 7 Segment LED에 숫자가 가상시뮬레이터에서 나왔던 결과와 동일하게 출력되는지 테스트한 결과입니다.


마무리


실제 아두이노우노로 실험을 해 보았습니다. 가상시뮬레터의 결과와 동일하게 나오는 것을 보실 수 있었을 겁니다. 이처럼 실제로 키트를 사서 실험을 하실 수 있지만 실제로 실험하면 가상으로 한것보다 시간도 많이 걸리고 손으로 일일히 노가다 작업을 많이 해야 합니다. 편하게 마우스로 선을 연결할 수 있는 것을 실제로 뻥판에 억지로 꼽고 하는게 쉽지가 않죠. 그리고 부품이 작어서 아무 지식도 없는 상태에서 보고 따라서 꼽을려고 하면 그것도 만만치 않습니다.

특히, 74HC595 칩의 경우는 작고 핀들의 네임들을 외우지 않는 이상 햇갈릴 수 있습니다. 하지만 가상에서는 마우스을 대면 핀 네임들을 쉽게 확인이 가능합니다. 가상시뮬레이터로 여러번 반복 실험을 하다보면은 실제 실험에서도 어렵지 않게 핀 연결을 할 수 있게 됩니다.

제가 아두이노우노를 실험할 때 실제 아두이노우노 키트가 있지만 가상시뮬레이터에서 하는 이유는 실제 아두이노키트를 사용해서 실험하는 것보다 가상시뮬레이터에서 더 쉽게 실험할 수 있기 때문입니다.

막연히 아두이노우노 키트를 사서 하는것보다 가상시뮬레이터로 충분히 즐긴 후 좀 더 다양한 실험을 하고 싶을때
아두이노 시리즈 중 원하는 싱글보드를 구매하셔서 실험하시면 됩니다.


댓글()

[아두이노] 직렬 연결한 쉬프트레지스터(74HC595) 제어

IOT/아두이노|2019. 2. 14. 09:32

[아두이노] 직렬 연결한 쉬프트레지스터(74HC595) 제어



이번에는 2개의 74HC595칩을 직렬로 연결하여 3개의 핀을 이용해서 2개 7 Segment LED를 제어하는 실험을 하였습니다. 이번에도 제 블로그에 포스팅 했던 내용인데 코딩은 최대한 원리를 설명하기 위한 로직이라서 기존 블러그에 있던 로직을 수정없이 그대로 소개하도록 하겠습니다. 여기다 살을 더 붙여서 60초 카운터 하는 로직을 설계할 수 있지만 그러면 군더더기 붙은 코딩으로 60초 변수를 만들고 if 조건문을 써서 십의자리와 일의자리를 쪼갤지 아니면 따로 카운터 변수로 정해서 조건문으로 표현할지 정하다 보면 본래 설명하고자하는 직렬 연결 설명이 복잡해 보일 수 있어 이번 포스팅은 간단히 어떻게 출력이 되는지만 실험하는 거라서 60초 카운터 출력은 독자의 몫으로 남겨 두겠습니다.

1. 직렬 쉬프트레지스터(75HC595) 연결



이전 시간에 75HC595 칩 하나를 아두이노에 연결하는 회도로를 이해했다는 전재하에서 기본 STCP, SHCP, DP 핀으로 해서 75HC595 칩을 제어를 하게 됩니다. 이 3핀을 제어하는데 직렬로 연결하면 위 그림처럼 연결을 하게 됩니다. STCP(결쇠)와 SHCP(클럭)핀은 공유합니다. DP핀은 오른쪽 75HC595 칩에 입력되고 그 입력된값이 Q7S 핀을 통해서 왼쪽 75HC595 칩의 DS 핀의 입력으로 들어 갑니다.

처음 아두이노에서 입력된 값이 16개의 값이 입력되면 앞에 8개 값이 왼쪽 75HC595 칩에 쌓이고 나머지 8개가 오른쪽 칩에 쌓여서 출력됩니다.

여기서 선색으로 대충 의미를 이해하시면 되겠습니다.

2. 회로도 구성


  • 준비물 : 75HC595칩 2개, 7 Segment LED 2개, 저항 330옴 2개, 아두이노우노, 뻥판
  • 내용 : 두개의 7 Segment LED에 숫자를 출력하게 하자.


74HC595 칩을 하나 사용할때도 복잡했는데 그림으로 2개를 사용하니깐 더 복잡해 보일꺼에요. 이전 시간에 한개로 했을때 선 연결과 동일하고 그걸 참고해서 두개를 동일하게 선 연결을 하시면 됩니다. 그리고 추가된것은 위의 직렬 쉬프트레지스터 연결을 보시면서 선이 왜 이렇게 연결되었는지 이해하시면 됩니다.

복잡해 보일뿐 단순합니다. 핀에 마우스를 대면 핀 이름이 나오고 해당된 부품과 연결하시면 됩니다.

3. 코딩


  • 사용함수 : pinMode(출력핀, 모드), digitalWrite(결쇠핀, 열림/잠금), shiftOut(데이터입력핀, 클럭핀, 순서, 데이터전송값)
  • 내용 : 아두이노에서 제공되는 shiftOut( )함수로 74HC595칩을 제어를 통해 2개 7 Segment LED에 숫자를 0과1, 1과2 ... 순차적으로 출력해보자.

  • 74HC595칩 제어 :
  • latchPin, clockPin, dataPin을 pinMode(출력핀, 모드)로 선언
  • digitalWrite(결쇠핀, 열림/잠금)으로 74HC595 칩에 입력할때 열림/잠금을 제어
  • shiftOut(데이터입력핀, 클럭핀, MSBFIRST, 전송값)으로 이걸 통해서 역순으로 데이터가 배치

설계

오른쪽 0부터 왼쪽 1부터 순차적으로 숫자를 출력

이전시간에

열림
쉬프트레지스트에 데이터 전송
닫침

코딩으로

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
digitalWrite(latchPin, HIGH);       

숫자 1개는 8개 값이 필요합니다. 그러면 쉽게 생각해보세요. 숫자가 2개면 16개의 값이 필요합니다. 그러면 쉬프트레지스트로 shiftOut()을 통해 두번 데이터를 전송하면 되겠다는것이 떠올라야 겠죠.
0과 1, 1과2 이렇게 순차적으로 출력하도록 설계할꺼니깐 아래과 같이 표현하면 되겠죠.

    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i+1]);
    digitalWrite(latchPin, HIGH);       

코딩을 하면

byte data[]={
0B10000001,  
0B11110010,
0B01001001,
0B01100000,
0B00110011,
0B00100100,
0B00000101,
0B11110000,
0B00000001,
0B00100000
};
int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop(){
  for(int i=0;i<9;i++){    
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i+1]);
    digitalWrite(latchPin, HIGH);       
    delay(1000);
  }  
}

4. 결과



마무리


3핀을 이용하여 2개의 74HC595 칩을 제어하여 2개의 7 Segment LED에 출력하였습니다.
회로도 보면 지져분하고 선연결이 복잡해 보일 수 있어요. 지난 시간에 배운 쉬프트레지스트 회로도를 디자인을 어느정도 이해하시면 2개든 3개든 보기에만 복잡해 보일뿐 참 쉬운 회로도입니다. 코딩도 지난시간에 했던 것에서 딱 한줄 늘어났을뿐 변화가 없습니다.

이 세줄만 이해하시면 몇개의 74HC595 칩을 사용해도 쉽게 해결됩니다.

열림
쉬프트레지스트에 데이터 전송
닫힘

오늘은 직렬 두개였으니깐

열림
쉬프트레지스트에 데이터 전송1
쉬프트레지스트에 데이터 전송2
닫힘

참쉽죠.

60초 카운터를 만들려 했는데 그러면 약간 직렬 제어 코딩에 혼동을 줄 수 있기 때문에 간단하게 두개를 어떻게 제어하는지만 배우는 것이 목적임으로 생략이 되었네요.

한번 여러분들이 60초 카운터 로직을 설계 해 보세요.
그리고 74HC595 칩의 원리로 어떤걸 표현하면 좋을지 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 쉬프트레지스터(74HC595) 제어

IOT/아두이노|2019. 2. 13. 14:32

[아두이노] 쉬프트레지스터(74HC595)



제 블로그에 포스팅 했던 내용인데 좀 어려운 내용이라서 이걸 소개해야하나 남감했지만 7 Segment LED의 연결되기 때문에 최대한 간단하게만 설명하겠습니다.

이전 시간에 7 Segment LED 제어를 배웠습니다. 이걸 사용하기 위해서 총 8핀을 아두이노에 연결해야 합니다. 문제는 2개 이상의 7 Segment LED를 제어할 경우 아두이노핀이 부족하게 됩니다. 그래서 아두이노 사용되는 핀을 줄일 수 있는 방법을 찾다보니깐 여러 칩중에 쉬프트레지스터라는 칩을 알게 되었으며 온라인 가상시뮬레이터에서도 모델명 75HC595칩(쉬프트레지스터)이 제공하기 때문에 이 칩을 이용해서 제어해보도록 하겠습니다.

1. 쉬프트레지스터(75HC595)


칩만보면은 햇갈립니다. 실제로 칩을 구매하실때 데이터시트를 꼭 인터넷에서 찾으셔서 살펴봐야 합니다. 물론 전 데이터시트를 보기가 참 힘들더군요. 간단히 칩구성과 핀 연결로 대충 이 칩의 작동원리만 이해하고 사용해 보았습니다. 참고로 칩을 사용하실때 칩에 사용되는 전압을 꼭 확인하시고 써야 합니다. 그렇지 않으면 칩이 망가질 수 있어서 조심해서 다루셔야 합니다.


  • 그림에서 보듯이 Vcc, Gnd은 그대로 해당 핀에 연결해주면 됩니다.
  • 데이터입력핀 DS핀 : 아두이노에서 데이터를 이칩에 보내게 됩니다.(총8개 출력값)
  • STCP 핀 : HIGH, LOW값으로 결쇠 역할을 하는 핀입니다. 칩에 입력할때 LOW 칩을 개방하고 입력을 끝내면 HIGH 칩닫음
  • SHCP 핀 : 클럭핀
  • MR 핀 : 5V 연결하더군요. 사용목적은 데이터시트 참고(저도 기억이 가물)
  • Q7S 핀 : 직렬 연결할때 사용합니다.이 칩 역시 8개값을 출력하기 때문에 더 많은 값을 출력하기 위해서 칩을 직렬 연결용으로 사용
  • Q0~Q7 : 각 핀으로 입력된 8개의 값을 출력합니다.

결론적으로 3개의 핀을 사용하여 결쇠핀, 클럭핀, 데이터핀으로 해서 이 칩을 제어할 수 있습니다. 가령 2개 이상의 75HC595를 사용해도 3핀만으로 직렬연결을 통해 제어가 가능합니다. 이건 다음에 소개하고 현재 이 칩을 아두이노에서 제어하는게 가장 중요합니다.

2. 회로도 구성


  • 준비물 : 75HC595 칩 1개, 7 Segment LED 1개, 저항 330옴 1개, 아두이노우노, 뻥판
  • 내용 : 숫자를 순차적으로 출력해보자.


칩이 정면으로 되어 있기 때문에 칩을 회전시켜야합니다. 그림을 잘 보시면 뒤집어져있는걸 보실꺼에요. 마우스로 해당칩에 끝점을 대면 핀이름이 나타납니다. 5V와 Gnd은 그림에서 보는것처럼 동일하게 배치하시면 됩니다.

참고로 Q0이 기름에서 보는것처럼 하단에 있어서 지난시간에 배운데로 A~G핀과 DP핀을 순차적으로 Q0~Q7까지 연결하면 선이 안이뻐서 Q0을 그냥 DP핀(점 LED)으로 했네요. 나머지 Q1~Q7을 A~G핀으로 연결했습니다. 그래서 배열변수값들이 지난시간에 제대로 이해 못하셨다면 약간 혼동되실꺼에요.

  • 7 Segment LED 제어 :
    74HC595칩의 Q0~Q7핀을 통해서 A~G핀과 DP핀 연결

3. 코딩


  • 사용함수 : pinMode(출력핀, 모드), digitalWrite(결쇠핀, 열림/잠금), shiftOut(데이터입력핀, 클럭핀, 순서, 데이터전송값)
  • 내용 : 아두이노에서 제공되는 shiftOut( )함수로 74HC595칩을 제어를 통해 7 Segment LED에 숫자를 순차적으로 출력해보자.

  • 74HC595칩 제어 :
    • latchPin, clockPin, dataPin을 pinMode( )로 선언
    • digitalWrite(결쇠핀, 열림/잠금)으로 74HC595칩에 입력할때 열림/잠금을 제어
    • shiftOut(데이터입력핀, 클럭핀, MSBFIRST, 전송값)으로 이걸 통해서 역순으로 데이터가 배치

설계

(1) 숫자패턴 배열변수 만든다.



7 Segment LED 연결된 것이 DP핀, A~G핀이 Q0~Q7순으로 순차적으로 연결됩니다. 홀짝에서 홀수일때 DP핀에 불이 들어오게 싶다면 숫자 0이면 G핀과 DP핀만 꺼지면 되니 10000001 이 됩니다. 그런데 1일때 0100111이겠지라고 생각하시면 안됩니다. MSBFIRST은 현재 연결핀 순서라면 역순이 됩니다. 11110010이 되어야 합니다.

이걸배열변수로 하면

byte data[]={
0B10000001,  
0B11110010,
0B01001001,
0B01100000,
0B00110011,
0B00100100,
0B00000101,
0B11110000,
0B00000001,
0B00100000
};

아마 지난시간을 생각하면 2차 배열변수로 생각하셨던 분도 있을꺼에요. 이건 하나의 값으로 표현이 가능합니다.
이걸 16진수로 하면 0x81 이 됩니다. 데이터숫자값으로 표현이 가능합니다. 이 같은 2진수 비트로 해서 다시 표현하면 위 배열처럼 나타나게 됩니다. 위 표현은 기계가 이해하기 쉬운 숫자형태입니다.

진법을 알아야하는 간단히 설명하자면
10000001 은 패턴이고 2진수입니다.
1000 0001로 4개씩 쪼개서 16진수로 나타냅니다.
8과 1로 표현됩니다.

0001=>1
0010=>2
0011=>3
.....
원래 8421 표를 숙지해야 하는데 현재 이것이 중요한것이 아니기 때문에 대충 네개로 묶으면 16진수 3개씩 묶으면 8진수라는 정도로 의미만 이해하세요.
16진수로 배열변수 값을 한다면 0x81로 표현해서 저장해도 동일한 결과가 나옵니다. 그냥 이해하기 쉽게 위 배열변수의 형식으로 이해하셔서 코딩하세요.
10진수가 아닌 기계가 이해하기 쉬운 숫자형태로 표현한다고 생각하세요.

(2) 74HC595칩에 원하는 숫자패턴을 전송한다.

  • 출력핀모드 선언 :
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);

(3) 74HC595칩에 숫자패턴을 입력한다.

digitalWrite(latchPin, LOW); //열림
shiftOut(dataPin, clockPin, MSBFIRST, data[i]); //패턴입력
digitalWrite(latchPin, HIGH); //닫힘   

(4) 7 Segment LED에 출력한다.

74HC595칩(Q0~Q7) => 7 Segment LED(DP,A~G) 출력

코딩을 하면

byte data[]={
0B10000001,  
0B11110010,
0B01001001,
0B01100000,
0B00110011,
0B00100100,
0B00000101,
0B11110000,
0B00000001,
0B00100000
};
int latchPin = 11; //ST_CP Pin
int clockPin = 12; //SH_CP Pin
int dataPin = 9; //DS Pin

void setup(){
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void loop(){
 for(int i=0;i<10;i++){
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
    digitalWrite(latchPin, HIGH);       
    delay(1000);
  }  
}

4. 결과



마무리


3핀을 이용하여 74HC595 칩을 제어하여 7 Segment LED에 출력하였습니다. 쉬프트 로직은 사실 좀 복잡합니다. 코딩 독해능력이 있어야 하는 부분임으로 그냥 함수명만 이해해 주세요. 위의 참고출처 가시면 튜토리얼로 쉬프트 함수의 로직을 살펴볼 수 있습니다. 독해능력이 되시면 어떤식으로 74HC595 칩에 데이터를 보내는지 확인하실 수 있을 꺼에요.

코딩에 자신있는분들은 본인이 직접 로직을 짜는분들도 있지만 초보분들은 그냥 있는 함수만 잘 사용하여 원하는 동작을 수행하는게 중요합니다.

digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, data[i]);
digitalWrite(latchPin, HIGH);       

이것이 코딩의 전부입니다. 이걸 통해서 74HC595 칩을 제어할 수 있어서 쉽게 원하는 글자나 숫자를 7 Segment LED에 출력을 할 수 있는 것이죠.

이 세줄만 이해하시면 됩니다.

열림
쉬프트레지스트에 데이터 전송
닫힘

이 문장만 숙지하시고 한번 이 세줄을 가지고 뭘 만들지 상상의 나래를 펼쳐보세요.


댓글()

[아두이노] 7 Segment LED 제어

IOT/아두이노|2019. 2. 12. 13:23

[아두이노] 7 Segment LED 제어



오늘은 LED 제어의 연장선상으로 7 Segment LED를 제어해 보도록 하겠습니다. 사용될 함수는 LED 제어때 사용한 pinMode( ), digitalWrite( )만 이해하시면 쉽게 제어를 할 수 있습니다.

1. 7 Segment LED


A,B,C,D,E,F,G,DP 핀으로 총 8개의 핀으로 구성되어 있습니다. 각 핀은 위 그림에서 보는것 같지 해당 위치의 LED에 불을 제어하는 핀으로 생각하시면 됩니다. 실제로 구매하시면 애노드형과 캐소드형이 있는데 위아래핀이 애노드 7 Segment LED이고 좌우고 캐소드였나 암튼 가상시뮬레이터에서 사용하는게 애노드형 초기 기본으로 되어 있는데 속성창에서 캐소드형으로 바꿔서 실험할 수 있습니다.

애소드와 캐소드 차이

7 Segment LED핀들이 가상시뮬레이터에서는 핀에서 COM1, COM2 핀이 있는데 아무핀이나 하나에 5V(+)핀이면 애노드형이고 Gnd(-)핀이면 캐소드형이 됩니다.

  1. 애노드 : A~G,DP핀이 0V이고 COM1 or COM2가 5V이면 해당 핀 LED에 불이 들어옵니다.
  2. 캐소드 : A~G,DP핀이 5V이고 COM1 or COM2가 Gnd이면 해당 핀 LED에 불이 들어옵니다.

쉽게 말해서 서로 반대라고 생각하시면 됩니다. 애노드형은 출력핀이 0일때 LED에 불이 들어오고 캐소드형은 출력핀이 1일때 LED에 불이 들어온다는 것만 이해하시면 됩니다. 참고로 7 Segment LED를 구매하실때 제품이 애노드형인지 캐소드형인지 확실히 확인하시고 구매하세요. 그래야 햇갈리는 핀연결과 코딩을 하지 않습니다.

2. 회로도 구성


  • 준비물 : 7 Segment LED 1개, 저항 330옴 1개, 아두이노우노
  • 내용 : 알파벳과 숫자를 출력해보자.

애노드형모델캐소드형모델



대충 붉은선이 5V에 연결되어 있고 검은선이 Gnd에 연결된 모습을 보면 대충 알겠죠.

##3. 코딩

  • 사용함수 : pinMode( ), digitalWrite( )
  • 내용 : 애노드형모델 기반으로 알파벳과 숫자를 순차적으로 출력하는 코딩을 해보자.

  • 7 Segment LED의 각 led 제어 :
    pinMode(핀, 모드)으로 전류를 출력할 핀들을 출력모드(OUTPUT) 사용하겠다고 선언
    digitalWrite(핀, 상태)은 HIGH(5V) or LOW(0V)로 전류를 출력할지 말지를 결정(여기서, 1은 5V, 0은 0V)

7 Segment LED도 내부에 LED들을 제어하기 때문에 LED 제어할때랑 동일합니다. 애노드형이기 때문에 0일때 불이 들어고 1일때 불이 꺼진다는 것만 생각하시고 코딩에 들어가세요. LED 제어의 복습차원이라고 생각하시면 됩니다. 참고로 알파벳과 숫자를 만드는 수작업인 노가다가 필요합니다.

설계

(1) 알파벳(A,B,C,D,E,F)과 숫자(0,1,2,3,4,5,6,7,8,9) 만들기



A글자 만들려면 LED D핀이 꺼지고 나머지 핀들이 켜져야 겠죠.(켜기=0, 꺼지기=1)

00010000

배열로 A[8]={0,0,0,1,0,0,0,0} 이 된다. 이건 하나의 A글자의 패턴입니다. 그러면 각 글자와 숫자의 패턴을 배열변수로 만들어야겠죠. 참고로, 앞에 7개는 글자나 숫자를 만드는 핀이고 마지막 8번째는 DP핀으로 점 LED 입니다. 0을 설정한 이유는 그냥 점 LED를 켜지는 것을 테스트 목적인것이지 의미는 없다는 점 참고하세요. 점 LED 빼고 싶다면 00010001 이러면 되겠죠.
아래 2차배열로 표현할때 점 LED은 글자가 출력할때 그냥 같이 켜지게 했고 숫자가 출력할때에 그냥 꺼지게 했어요. 원래 여러개 연결한다면 소숫점으로 사용하면 좋겠는데 한개 7 Segment LED라서 그냥 테스트로 켜지는지 보기위한 실험으로 의미는 없습니다.

int segValue[16][8] = {
   {0,0,0,1,0,0,0,0}, //A
   {1,1,0,0,0,0,0,0}, //B
   {1,1,1,0,0,1,0,0}, //C
   {1,0,0,0,0,1,0,0}, //D
   {0,1,1,0,0,0,0,0}, //E
   {0,1,1,1,0,0,0,0}, //F
   {0,0,0,0,0,0,1,1}, //0
   {1,0,0,1,1,1,1,1}, //1
   {0,0,1,0,0,1,0,1}, //2
   {0,0,0,0,1,1,0,1}, //3
   {1,0,0,1,1,0,0,1}, //4
   {0,1,0,0,1,0,0,1}, //5
   {0,1,0,0,0,0,0,1}, //6
   {0,0,0,1,1,1,1,1}, //7
   {0,0,0,0,0,0,0,1}, //8
   {0,0,0,0,1,0,0,1}  //9  
};

(2) 알파벳과 숫자를 만들었다면 실제 출력해야겠죠.

  • 핀배열 변수 선언 :
int segPin[8]={2,3,4,5,6,7,8,9}; 
  • 출력핀모드 선언 :
for(int i=0;i<9;i++){
    pinMode(segPin[i], OUTPUT);
  }
  • 출력 : 이부분이 실제 코딩 로직의 전부입니다. 각 패턴를 for문으로 루프 도는게 전부임
for(int i=0;i<16;i++){ //16패턴
  for(int j=0;j<8;j++){ //패턴값
   digitalWrite(segPin[j], segValue[i][j]);        
}
  delay(1000);
}  

애노드형모델 코딩을 실제 하면은

int segValue[16][8] = {
   {0,0,0,1,0,0,0,0}, //A
   {1,1,0,0,0,0,0,0}, //B
   {1,1,1,0,0,1,0,0}, //C
   {1,0,0,0,0,1,0,0}, //D
   {0,1,1,0,0,0,0,0}, //E
   {0,1,1,1,0,0,0,0}, //F
   {0,0,0,0,0,0,1,1}, //0
   {1,0,0,1,1,1,1,1}, //1
   {0,0,1,0,0,1,0,1}, //2
   {0,0,0,0,1,1,0,1}, //3
   {1,0,0,1,1,0,0,1}, //4
   {0,1,0,0,1,0,0,1}, //5
   {0,1,0,0,0,0,0,1}, //6
   {0,0,0,1,1,1,1,1}, //7
   {0,0,0,0,0,0,0,1}, //8
   {0,0,0,0,1,0,0,1}  //9  
};
int segPin[8]={2,3,4,5,6,7,8,9}; //사용핀{a,b,c,d,e,f,g,dp} 순서대로임

void setup() {

  for(int i=0;i<9;i++){
    pinMode(segPin[i], OUTPUT);
  }
}
void loop() {
  for(int i=0;i<16;i++){
    for(int j=0;j<8;j++){
     digitalWrite(segPin[j], segValue[i][j]);        
  }
    delay(1000);
  }  
}

for문에 대해서 저번에 설명을 했으며 for문을 이해해야만 왜 이렇게 코딩이 되었는지 이해가 되실꺼에요.
혹시 이해가 안되시는 분들은 LED 제어(아두이노)를 다시 한번 보시고 간단히 소개된 for문을 보시고 대충 어떤 느낌인지 살펴보세요.

여기서 캐소드형 경우는

패턴배열변수값들을 반대로 하시면 됩니다.

int segValue[16][8] = {
   {1,1,1,0,1,1,1,1}, //A
   {0,0,1,1,1,1,1,1}, //B
   {0,0,0,1,1,0,1,1}, //C
   {0,1,1,1,1,0,1,1}, //D
   {1,0,0,1,1,1,1,1}, //E
   {1,0,0,0,1,1,1,1}, //F
   {1,1,1,1,1,1,0,0}, //0
   {0,1,1,0,0,0,0,0}, //1
   {1,1,0,1,1,0,1,0}, //2
   {1,1,1,1,0,0,1,0}, //3
   {0,1,1,0,0,1,1,0}, //4
   {1,0,1,1,0,1,1,0}, //5
   {1,0,1,1,1,1,1,0}, //6
   {1,1,1,0,0,0,0,0}, //7
   {1,1,1,1,1,1,1,0}, //8
   {1,1,1,1,0,1,1,0}  //9  
};

코딩에서는 for문을 사용해서 led 출력핀들의 모드와 출력상태를 표현하는 것과 2차배열로 7 Segment LED패턴을 표현하는 것 두개뿐이 없습니다. 패턴을 2차배열로 표현하는 노가다 작업만 있을 뿐이죠.

4. 결과



애노드형 모델을 실험 동영상입니다.



마무리


LED 제어의 연장선상의 7 Segment LED제어를 해 보았습니다. 한개짜리지만 실제 4개짜리로 묶여진 부품도 있어서 좀 더 다양한 표현을 할 수 있습니다. 글자나 숫자를 출력할 수 있으면 이걸로 많은걸 표현이 가능합니다.

실생활에서 보면 시계를 떠오르시는분들이 많겠죠. 또는 거리에 나가면 가장 가깝게 신호등에 숫자가 출력되는걸 보실 수 있을꺼에요. 그러면 아두이노로 신호등을 만들고 7 Segment LED로 신호등의 타이머로 현실과 비슷하게 표현을 가능하겠죠. 이걸 대충요 종이로 신호등을 만들어서 제어하면 아이들에게 재밌는 교통 교육용이 될 수 있겠죠.

오늘 배운 패턴 배열변수는 무척 중요합니다. 꽤 많이 사용하는 2차배열변수인데요. 다음에 LED CUBE나 8x8 도트매트릭스를 소개할지 모르겠지만 계속해서 사용합니다. 배열변수를 혹시 모르시겠다면 관련글들을 검색해서 꼭 공부해주세요.


댓글()

[아두이노] 조도센서 제어

IOT/아두이노|2019. 2. 11. 13:34

[아두이노] 조도센서 제어



오늘은 간단히 조도센서를 제어하는 실험을 해보겠습니다. 조도 센서는 빛의 밝기에 따라서 전류값이 달라지는데 이 전류값을 아날로그핀에서 입력으로 받아서 빛에 의해서 어떤 행동을 하기 위한 곳에서 사용할 수 있는 재밌는 센서입니다. 쉽게 생각하시면 가로등을 예로 들 수 있겠죠. 밤이되면 조도센서를 이용해서 가로등을 자동으로 켜지게 할 수 있겠죠.

1. 회로도 구성


  • 준비물 : green led 1개, 조도센서 1개, 저항(220옴) 1개, 저항(10k옴) 1개, 아두이노우노, 뻥판
  • 내용 : 특정 조도센서값을 기준으로 green led에 불이 들어오게 한다.

A모델(-)방향 입력값B모델(+) 방향 입력값



어둠(0) ~ 밝음(255)
어둠(255) ~ 밝음(0)

2. 코딩


  • 사용함수 : Serial.begin( ), Serial.print( ), Serial.println( ), map( ), analogRead( ), pinMode( ), digitalWrite( )
  • 내용 : A모델을 기반으로 조도값이 100이하면 불이 들어오게 코딩을 해보자

  • 시리얼모니터 출력 :
    Serial.begin(9600)으로 시리얼통신을 사용하겠다고 선언
    Serial.print(출력값)은 출력값을 아두이노 IDE 시리얼모니터에 값을 출력하고 새로운 라인으로 안넘어감
    Serial.println(출력값)은 출력값을 출력하고 새로운 라인으로 넘어감

자세히 알고싶다면 => 출처 : https://www.arduino.cc/en/Serial/Println

  • green led 제어 :
    pinMode(핀, 모드)으로 전류를 출력할 핀 13번을 출력모드(OUTPUT) 사용하겠다고 선언
    digitalWrite(핀, 상태)은 13번핀을 HIGH(5V) or LOW(0V)로 전류를 출력할지 말지를 결정

  • 조도센서 입력 :
    map(입력값,입력최소값,입력최대값,변환최소값,변환최대값)으로 입력(0~1023)값을 자동으로 매핑시켜 (0~255)으로 변환시킨다. 즉, 알아서 크기에 맞게 값을 잡아준다고 생각하시면 됨.
    analogRead(아날로그입력핀) 전류의 값을 읽어옴

설계

  1. 조도센서를 읽어옴
    int m_cds = map(analogRead(cdspin),0,1023,0,255);
  2. 조도센서의 값이 100이하면 led가 켜지게 한다.
    if(m_cds){
    digitalWrite(greenpin, HIGH);
    }
    else {
    digitalWrite(greenpin, LOW);
    }

A모델 코딩을 실제 하면은

int greenpin = 13;
int cdspin = A0;

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

void loop()
{
  int m_cds = map(analogRead(cdspin),0,1023,0,255);
  
  Serial.print("CDS =  ");
  Serial.println(m_cds);
  
  if(m_cds<100) { 
     digitalWrite(greenpin, HIGH);  
  }
  else {
      digitalWrite(greenpin, LOW);  
  }
  
}

0~1023으로 잡고 시작하고 싶다면 map()함수는 필요 없겠죠. 입력값을 자신이 원하는 값(0~255)의 범위로 추가로 만들어 보았어요.

그냥 아날로핀값을 읽어서 그 값을 if문으로 해서 led에 불이 들어오게 할지 말지만 정하면 되니깐 위 코딩에서 절반이상 코딩량을 줄어 듭니다. 정확히 실험을 하기 위해서 그 값을 시리얼모니터에 줄력하기 위해 코딩이 늘어나고 또 입력된 값을 특정값의 범위로 맞추고 싶을때 map()함수가 추가되었을뿐 제어는 간단합니다.

if(analogRead(cdspin) < 300) digitalWrite(greenpin, HIGH);  

else  digitalWrite(greenpin, LOW);  

딱 두문장으로 끝낼 수 있죠. 원리는 이 두문장이고 약간 조미료를 추가한것이 A모델 코딩입니다. 원리만 이해하셔도 돼요.

결과



마무리


오늘은 간단한 실험을 해 보았습니다. 조도센서는 빛에 따라서 값의 변화가 일어납니다. 실제로 전류의 변화가 일어납니다. 이 전류값은 아날로핀으로 0~1023사이의 값으로 읽어오게 됩니다. 이 값을 기반으로 아두이노에서 제어를 하게 됩니다. 쉽게 떠오르는 것은 가로등이 있겠죠.

여러분들은 조도센서로 뭘 하고 싶으신가요. 예술가를 예를 들어볼께요. 어떤 작품을 만들었는데 그 작품에 빛에 반응한다고 생각해보세요. 그리고 RGB led를 작품에 부착한다면 조도센서값들에 따라서 작품은 다체로운 색상들을 만들어 낼 수 있다면 관람객과 소통하는 작품을 구현할 수 있겠죠.

또는 조도센서를 언제쯤 어두워지고 언제쯤 밝아지는지 데이터를 매일 수집하면 빅데이터로 활용도 가능해지겠죠.

한번 조도센서를 사용한다면 어디에다 사용할지 상상의 나래를 펼처보세요.


댓글()

[아두이노] 거리측정센서로 3D 랜더링을 할 수 있을까?

IOT/아두이노|2019. 2. 10. 15:11

[아두이노] 거리측정센서로 3D 랜더링을 할 수 있을까?


영화 출처 : 프로메테우스

영화 프로메테우스의 한장면의 일부입니다. 동굴 탐사를 하기 위해서 드론같은 비행물체를 2개 띄우는 장면으로 붉은빛을 주기적으로 동굴벽에 비추는데 영화상에서 센서의 하나겠지요. 비행물체는 비행하면서 붉은 빛을 비추면서 동굴의 정보를 추출하게 됩니다. 그리고 우주선 본체에서 추출된 정보를 통해서 동굴 통로를 3D 랜더링을 하게 됩니다.

빛이 비추는 지점에 대한 거리 정보를 추출하지 않을까 생각되네요. 비행물체를 계속 이동하면서 일정각도와 일정거리로 이미지와 거리측정을 통해 각 벽면의 좌표점을 추출하지 않을까 생각됩니다.

그렇다면은 아두이노로 표현을 한다면 과연 어떻게 할까요 한번 생각해 봅시다.

1. 아두이노로 구현한다면 어떻게 할까요?


이미지 정보를 빼고 거리정보만으로 정보를 추출한다면 우선 가설은 이렇습니다. 비행물체는 아두이노드론으로 설정하고 드론본체의 중심에 초음파센서들을 부착하고 360도 거리 정보를 추출한다고 가정해 봅시다.


동굴 A에서 B까지 1도씩 360도의 거리 정보를 초음파센서로 각 좌표 정보를 추출하게 만든다면 원을 그린 것처럼 360개의 벽과의 거리 정보를 추출하게 됩니다. 그러면 드론의 현재 기준위치 정보에서 하나의 단층 좌표점들을 추출할 수 있겠죠. 그러면 이런 좌표이미지를 단층으로 해서 계속 누적시키면 어떻게 될까요. 드론이 일정간격 마다 벽과의 거리 꼭지점 단층 이미지들이 만들어 지겠죠. 가령 수백장의 단층이미지를 얻었다고 치면 여기서 각 꼭지점을 연결하면 3D 랜더링을 하게 되면 3차원 모델링 된 이미지를 얻을 수 있겠죠.

2. 대충 꼭지점을 연결한다면 이런 느낌!



각 꼭지점 사이의 거리는 드론이 일정이동거리이고 세로축은 초음파센서로 거리를 측정하는 지점이 되겠죠. 꼭지점은 가로의 한면만 나타낸 것이죠. 여기서 세로로 360개의 꼭지점을 얻을려면 x,y은 드론의 위치 좌표이고 z은 벽과의 거리로 (x,y,z)의 꼭지점을 360개 만들어 낼 수 있고 이정보는 세로 단층 이미지를 만들어 낼 수 있게 됩니다. 이 정보는 3D 랜더링을 하기위한 기본 정보가 되겠죠.

여기서 세로축으로 바라보면은 아래와 같은 모습이겠죠. 드론은 x,y 좌표가 되고 그 기준으로 z은 벽과의 거리가 되겠죠.


이 좌표들을 드론이 일정이동 간격으로 측정하하면서 이동하고 그정보를 중앙컴퓨터가 계산하여 좌표를 3D 랜더링을 하게 하면 동굴의 3D 이미지를 만들 수 있겠죠.

3. 영화는 왜 한축을 기준으로 회전해서 빛을 안쐈을까?


단순히 기준점으로 세로축으로 360도 회전해서 측정한다고 생각해 보세요. 그러면 어떤 문제가 생길까요. 바로 동굴은 매끈한 원형이 아니고 불규칙한 모습 때문입니다.


보는것과 같이 사각지대가 발생합니다. 가려진 부분은 그냥 이으면 빈공간이여도 매꾸어져 버립니다. 그래서 중심축으로 360도 회전이 아니라 영화에서 처럼 전망과 후방과 그리고 45도 각도로 해서 센서가 거리를 측정하게 됩니다.

비행물체가 일정간격으로 이동할때 마다 일정한 패턴으로 측정이 이뤄지는데 이동전과 이동후의 측정된 거리정보에서 후방측정과 후방 45도 측정시 그 각도지점은 이동전 (x,y) 좌표에 측정된 단층정보에 새롭게 측정된 사각지대 정보를 추가로 덮어 씌운다면 보다 정확한 단층이미지가 생성되겠죠. 그래서 영화에서는 전방, 후방, 45도 각도별 거리 정보를 추출하는 장면을 보여주기 위해서 붉은 빛으로 패턴을 보여준것 같다는 생각이 듭니다.

쉽게 말해서 영화에서는 일정간격으로 앞좌표, 현재좌표, 다음좌표의 정보를 거리 정보를 추출하여 그 정보를 종합해서 3차원 이미지를 만들어 낸 것이죠.

4. 비행물체의 비행은 어떻게 할까?


무인 자동비행을 해야하기 때문에 드론의 비행 좌표는 동굴의 중심으로 해서 날아가야 합니다. 하지만 동굴은 동글한 모양이 아니기 때문에 계산해야겠지요. 어떻게 해서 날아가야할까요. 위치정보 센서와 같은 기타 센서등을 이용도 해야겠지만 거리측정만으로 한다고 가정해 봅시다.


비행물체 전방에 거리측정센서를 부착해서 전방의 측정된 좌표 거리값 중 임의의 A,B의 중 가장 가까운 값을 기준으로 그 값을 2로 나누는 지점이 중심거리가 되고 그 지점을 (x,y,z)의 비행 좌표로 하면서 이동하면 되겠죠.

마무리


이건 초음파센서만을 이용해서 설정한 가설입니다. 거리측정 센서는 다양합니다. 중요한것은 거리를 측정할 수 있으면 할 수 있는 것들을 설명하기 위해서 영화의 한 대목을 한번 아두이노로 표현한다면 어떻게 할 수 있을까의 상상의 나래입니다.

그리고 3D 랜더링하는 기술은 소개하지 않았습니다. 공간 꼭지점(x,y,z)를 구할 수 있으면 그 점들을 이으면 폴리건(면)을 만들 수 있습니다. 이건 컴퓨터그래픽스 프로그램언어 영역이라서 주제에서 벗어남으로 그 부분에 대한 설명을 생략합니다. 저 또한 3D 랜더링 기술을 한때 공부한적은 있습니다. 지금은 알고리즘도 기억 안나네요.

원래 주제를 초음파레이더를 소개할까 했지만 아직 서보모터 포스팅이 들어가지 않아서 아쉽게도 초음파레이터는 다음에 시간이 되면 소개할까 합니다.

물론 구글 검색하면 동영상강좌나 블로그에 초음파레이더가 쉽게 검색되고 꽤 유명해서 자료가 많습니다. 중고생들 실험 소재로 많이 사용합니다. 스크래치를 이용한 초음파레이더나 프로세싱을 이용한 초음파레이더 예제는 차고 넘쳐서 궁금하신분들은 구글 검색을 통해서 찾아보세요.

우선 거리를 측정할 수 있다면 뭘 할 수 있을까 중요합니다.

암튼 거리측정이란 단어를 한번 머리속에서 상상의 나래를 펼쳐 보세요. 그리고, 아두이노를 공부하실때 그냥 대충 남들이 만드니깐 따라 만들지 마시고 SF 영화같은데 보면서 저장면을 아두이노로 표현한다면 과연 어떻게 표현이 될까하고 상상을 하면 많은것들이 떠오르게 됩니다. 거리측정센서를 처음 공부할때 거리측정기를 기준점(x,y)가 되면 거리를 깊이(z)로 해서 공간 좌표를 추출할 수 있을 것이고 이를 통해서 3D 랜더링도 가능하지 않을까 하고 상상의 나래를 펼친적이 있네요. 그리고 프로메테우스란 영화가 떠오르더군요. 별거 아닐것 같은것들이 꽤 재밌는 발상을 하게 되면 재밌는 작품들이 만들어 집니다.

거리측정으로 할 수 있는것을 저처럼 한번 상상의 나래를 펼처보세요.

댓글()

[아두이노] 초음파줄자 응용

IOT/아두이노|2019. 2. 9. 12:46

[아두이노] 초음파줄자 응용




초음파센서로 줄자를 만들려면 대충 이런식으로 A, B의 벽까지의 거리를 측정하면 쉽게 줄자가 만들어 질 수 있겠죠.
이걸 표현하기 위해서 실험을 하고자 합니다.

1. 회로도 구성


  • 준비물 : 초음파센서3핀 2개, 스위치버턴 1개, 아두이노우노, 뻥판
  • 내용 : 벽과 벽사이의 거리를 측정하는 줄자를 구현해보자.

스위치를 누르면 아두이노우노 본체를 기준으로 양쪽 두지점의 벽과 벽사이 거리를 측정하도록 하기 위해서 회로도를 우선 아두이노우노 기준으로 양쪽 방향으로 초음파센서를 배치하여 회로도를 구성하였습니다.


2. 코딩


  • 거리계산 : ((float)(340 * 초음파거리시간값) / 10000) / 2;
  • 두지점거리계산식 : 아두이노우노를 10cm로 가정할경우
    total=v1+v2+10; => v1(A초음파), v2(B초음파), 10(아두이노우노본체크기)
  • 내용 : 스위치를 누르면 A,B 초음파 거리를 측정하고 그 결과를 시리얼모니터에 출력한다.

우선 스위치버턴은 복습차원으로 내부풀업저항을 이용합니다. 그래서 pinMode은 INPUT_PULLUP으로 선언합니다. 그리고 스위치 버턴값을 읽기 위해서 digitalRead(핀번호) 함수를 사용합니다. 지난 스위치버턴 포스팅에서 이미 소개했으며 복습차원으로 스위치 버턴을 사용하였습니다.

void setup() {
  Serial.begin(9600);
  pinMode(5,INPUT_PULLUP);
}
void loop() {  
  if(digitalRead(5)==0){
    float v1=UltrasonicDistance(6);
    float v2=UltrasonicDistance(7);
    float total=v1+v2+10;
    Serial.println(total);
    delay(500);
  }  
}

float UltrasonicDistance(int m_pin){
  pinMode(m_pin,OUTPUT); 
  digitalWrite(m_pin, LOW); 
  delayMicroseconds(2); 
  digitalWrite(m_pin,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(m_pin,LOW); 
  
  pinMode(m_pin,INPUT);    
  float duration = pulseIn(m_pin, HIGH);  
  return duration / 57.5;  
}

여기서 2개의 초음파 거리측정을 해야하는데 그러면 두번의 중복 코딩이 발생합니다. 지난시간에 배웠던 C언어문법 외부함수로 재정의하여 뺐습니다.

복습하자면

float 초음파거리(int A){
 return 거리식;
}

초음파거리함수에 인자는 핀번호입니다. 함수 앞에 float는 변수 선언에서 설명한 자료형입니다. 즉, 함수 앞에서 자료형이 표현되었다는 것은 return의 명령어로 자료형(float)으로 반환한다는 의미입니다. 여기서는 좀 더 정확한 거리계산을 위해서 실수형으로 거리 계산된 값을 반환하겠다는 의미인거죠. 만약 정수형(int, long)으로 반환하겠다면 앞에 정수자료형을 선언하시면 됩니다.

loop()함수에서 사용할때는

<
v1=초음파거리(6)

이렇게 하면 6번핀의 초음파센서가 거리계산을 한뒤에 그 값을 반환하여 v1에 저장하게 됩니다.
v1 6번핀에 연결된 초음파센서 거리값이 저장됩니다.

초음파거리 계산식을 외부함수로 재정의함으로써 중복코딩을 줄이게 됩니다.
loop()안에는 아두이노 전원이 공급되고 반복되는 작업 명령들이 수행됩니다. 가독성을 위해서는 될 수 있으면 loop()함수에 선언되는 코딩들은 최소화 해야합니다. 그래야 가독성이 좋고 나중에 수정하기도 편합니다.

코딩 순서는

  1. 스위치버턴을 누르면 거리를 계산한다.
    pinMode(5,INPUT_PULLUP); //선언
    if(digitalRead(5){ //동작
    거리계산;
    }
  2. A, B초음파 거리 계산측정한다.
    float v1=UltrasonicDistance(6); //A 초음파거리계산
    float v2=UltrasonicDistance(7); //B 초음파거리계산
  3. 최종거리 계산한다.
    float total=v1+v2+10;
  4. 시리얼모니터에 그 결과를 출력한다.
    Serial.begin(9600); //선언
    Serial.println(total); //출력

3. 결과



마무리


코딩은 우선 어떤 명령을 내릴지 한글로 메모장에다가 순서를 정하여 글로 써보세요. 그리고 그 명령에 대해 사용할 함수들은 각 부품을 다룰때 쓰던 함수들을 그대로 사용하시면 됩니다. 어렵게 생각하실 필요없이 각 부품을 소개할때 거기에 사용한 함수들 2~3개를 기억했다가 그 함수의 의미를 이해하고 그것을 응용해서 표현하시면 됩니다.

정교한 초음파센서를 이용해서 건축현장에서 건물안에 길이를 측정할 때 유용하겠죠.

또 초음파로 어떤것을 표현할 수 있을까요. 상상의 나래를 펼쳐 보세요.


댓글()

[아두이노] 거리경보장치 응용

IOT/아두이노|2019. 2. 7. 18:50

[아두이노] 거리경보장치 응용



예전에 실험한 내용인데 초음파 응용편으로 소개할까 합니다. 거리경보장치는 실생활에 가장 유사한것이 자동차 후진 주차할때 경보장치를 연상하시면 될꺼에요. 뒤에 장애물과의 거리가 위험할때 나는 경보음을 생각하시면 아마 이해가 빠를꺼에요.

1. 회로도 구성


  • 준비물 : 초음파센서3핀 1개, led 2개, 저항 220옴 2개, 피에조부저 1개, 아두이노우노, 뻥판
  • 내용 : 장애물과의 거리가 50cm미만일때 경보음과 Red Led에 불이 들어오고 위험을 알리고, 50cm이상일때 Green Led에 불이 들어오고 안전상태를 표현합니다.


2. 코딩


  • Led : 선언-pinMode(핀번호, OUTPUT) 제어-digitalWrite(핀번호,상태값); 로 상태값 1(5V) or 0(0V)

  • 피에조부조 : 제어-tone(출력핀,음계,음길이), noTone(출력핀)으로 음과 음사이를 끊어줌

  • 초음파센서 : 선언pinMode(핀번호, 입/출력모드), delayMicroseconds(시간값)으로 마이크로초로 잡게 딜레이 시킴, pulseIn(7, HIGH)로 초음파가 장애물에 부딪치고 되돌아온 거리시간값을 입력받음

  • 거리계산 : ((float)(340 * 초음파거리시간값) / 10000) / 2;


코딩 설계는

  1. 초음파센서 거리 측정한다.
  2. Led 2개로 안전/위험 상태를 나타낸다.
  3. 피에조부조에서 위험거리일때 경보음이 울리게 한다.

1번 코딩 :
지난시간의 초음파 거리측정 코딩을 그대로 적용한다. 하지만 안전/위험 상태를 기준을 50cm로 설정한다면 어떻게 코딩해야할까요. 지난시간에 배웠던 문법 if문 조건이 참/거짓으로 나뉘면 쉽겠죠.

  • 거리측정은 7번핀에서 pulseIn(7, HIGH)로 거리시간값을 ((float)(340 * 초음파거리시간값) / 10000) / 2 로 거리가 구함
  • if(거리<50) {처리문1; } else {처리문2;}

2번 코딩 :
led 2개로 led은 위험, green은 안전으로 1번 코딩에서 50cm미만일때 red led 켜고, 50cm이상일때 green led 켜면 되겠죠.

if(거리<50){
    digitalWrite(11, HIGH);  
    digitalWrite(10, LOW);  
}
else
{
   digitalWrite(11, LOW);  
   digitalWrite(10, HIGH);  
}

참쉽죠

3번 코딩 :
경보음은 피에조부저에서 tone(), noTone()함수를 어떤 음계로 음의길이를 어느정도 할지는 자유입니다. 그런데 어디에 코딩할까. 50cm이하 일때 경보음을 울려야 겠죠.

if(거리<50){
    tone(12,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);            
}
else
{
}

이렇게 해서 코딩은 간단히 해결되었습니다. 쪼개서 보면 별거 없습니다. 처음 기준이 되는 부품이 어떤 상태가 되면 다른 부품이 그 상태를 기준으로 어떻게 변화되는지만 글로 한번 설계해보시고 그걸 코딩으로 표현하시면 됩니다.

초음파센서로 거리가 50cm미만이면 위험상태(red led 켜고, 피에조부저 경보음)와 안전상태(green led 켜기)를 표현한 것 뿐이죠. 그런데 이걸 다 합쳐진 코딩을 보면 복잡해 보일 수 있습니다. 하지만 부품 한개 한개의 제어한 것들을 합치니깐 복잡해 보일뿐 부품을 개별적으로 생각하시면 아주 간단한 코딩입니다.

종합해서 코딩하면은

void setup() {  
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
}
void loop() {   
  pinMode(7,OUTPUT); //7번핀 출력모드로
  digitalWrite(7, LOW); 
  delayMicroseconds(2); 
  digitalWrite(7,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(7,LOW); 
  
  pinMode(7,INPUT);    //7번핀 입력모드로
  float duration = pulseIn(7, HIGH);
  float distance = duration / 57.5;  
  

  if(distance < 50){    //50cm 미만 경보발동

    digitalWrite(11, HIGH);  
    digitalWrite(10, LOW);  

    tone(12,523,1000/8);     // 도음으로 8분음표(음길이)
    delay(1000/4*1.30);             
    noTone(12);            

  } else{                   //50cm 이상 안정거리
    digitalWrite(11, LOW);  
    digitalWrite(10, HIGH); 
  } 
}  

3. 결과



마무리


led, 피에조부저, 초음파센서를 각각 이전 시간에 배웠고 그걸 합쳐서 하나의 경보장치를 만들어 보았습니다.
또, 어떤것들이 있을까요 한번 상상의 나래를 펼쳐보세요.

초음파센서가 은근 재밌는 소재라 또다른 응용편을 다음에 소개 할께요.


댓글()

[아두이노] 초음파센서 제어

IOT/아두이노|2019. 2. 6. 11:01

[아두이노] 초음파센서 제어



오늘은 재밌는 소재로 거리를 측정할 수 있는 초음파센서를 사용하여 실험하는 내용을 다뤄 보겠습니다. 실제 초음파센서를 구매하시면 4핀으로 구성되어 있는데 가상시뮬레이터에서는 초음파센서가 3핀으로 되어서 어떻게 코딩해야하나 혼동 되실 수 있지만 원리는 동일하니깐 어렵게 생각하실 필요는 없습니다.

1. 초음파센서



초음파센서를 부품을 실제 구입하면 1200원정도 하는 초급 초음파센서를 구매하실수 있을꺼에요. 핀은 총 4핀으로 전원(+,-) 2핀, 입력핀, 출력핀으로 구성되어 있습니다. 하지만 가상시뮬레이터에서는 3핀으로 구성되어 있으면 입력/출력을 한핀에서 다 제어해야 합니다.

가상 시뮬레이터 코딩은 이 한핀을 어떻게 제어할지만 생각하시고 표현하시면 됩니다.

2. 초음파센서 계산 공식


  • 공식 : 기본 340 (위 1200원짜리 4핀 초음파센서 초음파속도입니다.)
    거리 = ((초음파속도 * 센서시간값) / 10000) / 2

            ((float)(340 * duration) / 10000) / 2;  
    

초음파속도 : 실제 사용하는 초음파센서 부품의 초음파속도입니다. 즉 초음파센서마다 초음파속도는 다르고 부품을 구매하실때 제공되는 정보를 잘 확인하시면 정교한 계산을 하실 수 있을꺼에요.

센서시간값 : 초음파센서에서 초음파가 나가고 앞에 장애물에 부딪치고 되돌아오는데 까지 걸리는 시간값입니다. 왕복시간값이라고 생각하시면 됩니다. 그래서 마지막에 나누기 2를 함으로써 초음파센서의 위치에서 장애물까지의 거리가 나오게 되는 것이죠.

3. 초음파센서 코딩

  digitalWrite(7, LOW); 
  delayMicroseconds(2); 
  digitalWrite(7,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(7,LOW); 
   
  float duration = pulseIn(6, HIGH);
    
  float distance = ((float)(340 * duration) / 10000) / 2;  
    

7번핀이 출력모드이고 6번핀은 입력모드입니다. 7번핀으로 초음파를 쏘고 되돌아오는 초음파를 6번핀에서 읽게 됩니다. 그 값은 시간값이고 초음파공식에 대입하여 계산하게 됩니다.

  1. 초음파를 쏘기 전 초음파출력핀을 LOW(0)으로 한다. => 초기단계
  2. 딜레이 delayMicroseconds()함수는 시간을 마이크로초로 딜레이 시킬 때 사용. => 대기시간단계
  3. 초음파출력핀을 HIGH(5V)으로 한다. => 초음파를 출력 시작
  4. 딜레이 delayMicroseconds()함수로 대기한다. => 초음파를 쏘는 시간이다.
  5. 초음파출력핀을 LOW(5V)으로 한다. => 초음파 출력 중지

쉽게 말해서 초기상태는 초음파 출력핀 0V 상태로 2마이크로초로 초기상태로 뒀다가 7번핀을 통해 10마이크로초동안 초음파를 쏘고 난뒤에 초음파 출력핀 0V으로 10마이크로초동안만 초음파를 쏜 신호값을 거리계산에 사용한다는 점만 이해하시면 된다.

pulseIn(입력핀, HIGH)함수 : 펄스 신호의 길이를 잴 때 사용합니다. 초음파 되돌아 왔을때 HIGH->LOW가 되는 시점까지의 시간값을 반환하는 함수입니다. 좀 어려울 수 있습니다.
그냥 저 함수로 초음파 거리 시간값을 읽어오는 구나 정도만 처음에 이해하시면 됩니다.

정 모르겠다면 통으로 저 코딩을 외우시면 됩니다. 초음파센서를 사용할때 저 코딩으로 제어하는 구나 정도만 이해하시면 됩니다.

4. 회로도 구성


  • 준비물 : 초음파센서3핀 1개, 아두이노우노

5. 코딩


void setup() {
  Serial.begin(9600);
}
void loop() {   
  pinMode(7,OUTPUT); //출력모드  초음파 출력
  digitalWrite(7, LOW); 
  delayMicroseconds(2); 
  digitalWrite(7,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(7,LOW); 
  
  pinMode(7,INPUT);    //입력모드로 초음파 입력
  float duration = pulseIn(7, HIGH); //초음파 거리 시간값 읽기
  float distance = duration / 57.5;  // ((float)(340 * duration) / 10000) / 2;   

  Serial.println(distance);   
} 

핀의 입력/출력모드 setup()함수에 선언된다고 했는데 여기서는 loop()함수에서 선언하였습니다. 왜 그렇게 했을까요. 그것은 바로 7번핀을 입력모드와 출력모드를 둘 다 사용해야 하기 때문에 계속 핀모드를 변경해야 합니다. 그래서 loop()함수에 선언된거죠.

초음파를 출력하기전에 7번핀을 출력모드로 선언했다가 다시 7번핀을 pulseIn()함수로 입력을 받을때에는 그 앞에 7번핀을 입력모드로 선언해야겠죠. 이런식으로 해서 7번핀으로 입력/출력모드 제어가 가능해 지는 것이죠.

 float distance = duration / 57.5;  // ((float)(340 * duration) / 10000) / 2;   

이식은 공식에서 최대한 줄이고 가상시뮬레이터에서 좀 더 거리가 비슷하게 나타내기 위해서 근사값 57.5로 표현했습니다. 실제로 하실때는 정식 공식을 사용하세요. 가상시뮬레이터에서는 정식 공식으로 대입하면 오차 거리가 좀 크게 나서 일부러 공식을 줄이고 줄인 공식에서 숫자를 앞뒤로 근사값들을 임의로 정해서 여러차례 실험한 뒤에 가장 오차가 적게 나오는 값이 57.5여서 이렇게 식을 만들었습니다.

실제로 하실때에는 정식 공식을 사용하시고 정식 공식을 써도 오차 거리가 발생할때에 그 부품에 맞게 근사값들을 잡아서 오차 거리를 줄이시면 아마 될꺼에요.

시리얼모니터 출력

지난시간에 사용했지만 복습차원으로 다시 설명하자면

아두이노 IDE 시리얼모니터를 사용하기 위해서

void setup()
{
 Serial.begin(9600);
}
void loop(){
Serial.println(distance);   
}

setup()함수에서 시리얼 통신을 시작한다고 선언(Serial.begin) 하고 loop()함수에서 시리얼모니터로 출력(Serial.println) 합니다.

Serial.println(출력값);   

이정도만 우선 알아두세요.

6. 결과

마무리


실제로 한다면 4핀 코딩을 해야하는데 아래와 같이 실험하시면 됩니다.

void setup() {
  Serial.begin(9600);
  pinMode(7,OUTPUT); //출력모드
  pinMode(6,INPUT);    //입력모드
}
void loop() {    
  digitalWrite(7, LOW); 
  delayMicroseconds(2); 
  digitalWrite(7,HIGH); 
  delayMicroseconds(10); 
  digitalWrite(7,LOW); 
   
  float duration = pulseIn(6, HIGH);
  float distance = duration / 57.5;  // ((float)(340 * duration) / 10000) / 2;  

  Serial.println(distance);   
}  

이 초음파센서 하나로 어떤곳에 사용하면 좋을까요 한번 상상의 나래를 펼쳐보세요. 초음파센서가 거리를 측정할 수 있는 재밌는 부품이여서 거리를 측정할 수 있으면 할 수 있는 것들이 참 많습니다. 한번 생각해 보세요.


댓글()

[아두이노] 곰세마리멜로디(피에조부저)

IOT/아두이노|2019. 2. 5. 14:32

[아두이노] 곰세마리멜로디(피에조부저)



1. 악보


2. 멜로디 배열변수 만들기

음계+음길이

위의 악보에서 음계랑 음길이를 메모장에서 간단히 표시해 보자.

도도도도도
4 8 8 4 4
미솔솔미도
4 8 8 4 4
솔솔미솔솔미
8 8 4 8 8 4
도도도
4 4 2

솔솔미도
4 4 4 4 
솔솔솔
4 4 2
솔솔미도
4 4 4 4 
솔솔솔
4 4 2

솔솔미도
4 4 4 4
솔솔솔라솔
8 8 8 8 2
도솔도솔
4 4 4 4
미레도
4 4 2

5옥타브음계

5옥타브 음계 도레미파솔라시도 이것만 이용 합니다.

5옥타브 음계에서 벗어난 음계를 사용할 경우 위 사이트 가셔서 다른 옥타브 음계를 참조하셔서 사용하시면 됩니다. 귀찮은 분들은 전음계를 다 복사해와서 만드셔도 됩니다.

#define NOTE_C5  523   //도
#define NOTE_D5  587   //레
#define NOTE_E5  659   //미
#define NOTE_F5  698   //파
#define NOTE_G5  784   //솔
#define NOTE_A5  880   //라
#define NOTE_B5  988   //시
#define NOTE_C6  1047  //도

멜로디배열변수

계이름을 매크로변수(#define) 이름으로 배열변수에 저장하시면 됩니다. 그냥 도를 523으로 저장해도 되지만 이름으로 표현하시면 음계를 쉽게 구별할 수 있기 때문에 숫자보다는 변수 이름으로 만들어서 코딩하는걸 추천 드려요.

int melody[] = {
NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,               //도도도도도
NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,               //미솔솔미도
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,       //솔솔미솔솔미
NOTE_C5,NOTE_C5,NOTE_C5,                               //도도도
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,                       //솔솔미도
NOTE_G5,NOTE_G5,NOTE_G5,                               //솔솔솔
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,                       //솔솔미도
NOTE_G5,NOTE_G5,NOTE_G5,                               //솔솔솔
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,                       //솔솔미도
NOTE_G5,NOTE_G5,NOTE_G5,NOTE_A5,NOTE_G5,               //솔솔솔라솔
NOTE_C6,NOTE_G5,NOTE_C6,NOTE_G5,                       //도솔도솔
NOTE_E5,NOTE_D5,NOTE_C5                                //미레도
};

멜로디음길이배열변수

각 음계의 음길이가 각기 다르기 때문에 4분음표, 8분음표, 2분음표 등의 박자길이를 숫자로 해서 멜로디음길이배열변수를 만들어서 순차적으로 음계랑 음길이를 나중에 코딩에서 합쳐서 음을 만들어 내기 위해서 배열변수로 해서 음계과 음길이를 따로 만듭니다.

int noteDurations[]={
4,8,8,4,4,
4,8,8,4,4,
8,8,4,8,8,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
8,8,8,8,2,
4,4,4,4,
4,4,2
};

3. 회로도 구성


준비물 : 피에조부저 1개, 아두이노우노


지난 시간의 회로도와 동일합니다.


4. 코딩


지난 시간의 코딩에서 2번의 멜로디를 만든 배열변수를 추가를 하여 약간 코딩을 변경하시면 됩니다.

#define NOTE_C5  523    //도
#define NOTE_D5  587    //레
#define NOTE_E5  659    //미
#define NOTE_F5  698    //파
#define NOTE_G5  784   //솔
#define NOTE_A5  880   //라
#define NOTE_B5  988   //시
#define NOTE_C6  1047 //도


int tonepin = 12;


int melody[] = {
NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,NOTE_C5,
NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_G5,NOTE_G5,NOTE_E5,
NOTE_C5,NOTE_C5,NOTE_C5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,
NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,

NOTE_G5,NOTE_G5,NOTE_E5,NOTE_C5,
NOTE_G5,NOTE_G5,NOTE_G5,NOTE_A5,NOTE_G5,
NOTE_C6,NOTE_G5,NOTE_C6,NOTE_G5,
NOTE_E5,NOTE_D5,NOTE_C5
};

int noteDurations[]={
4,8,8,4,4,
4,8,8,4,4,
8,8,4,8,8,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
4,4,2,
4,4,4,4,
8,8,8,8,2,
4,4,4,4,
4,4,2
};


void setup() { 
} 
void loop() {
  for (int i = 0; i < 49; i++) {
    
    int Durations = 1000/noteDurations[i];    // 음계의 음길이 계산
    tone(tonepin, melody[i], Durations);    
    int pauseBetweenNotes = Durations *1.3 ;
    delay(pauseBetweenNotes);
    noTone(tonepin);
  }

}

이번에는 loop()에 반복수행하도록 한번 표현해봤습니다.

int Durations = 1000/noteDurations[i];    // 음계의 음길이 계산

음의 출력이 melody[i], noteDurations[i]로 매칭되어 음이 나오게 됩니다.

음길이 계산식이 원래 고정 1000/4여서 외부에 변수로 표현했지만 실제 곰세마리악보에서 보듯이 음길이가 다르기 때문에 melody[i], noteDurations[i]를 맞추기 위해서 for문 안에다 표현을 하였습니다.

그리고 보기 좋게 하기 위해서 따로 Durations로 변수를 만들어서 i번째 음길이를 가져와서 음길이를 계산한 표현을 하였습니다. 물론 tone(tonepin, melody[i], 1000/noteDurations[i])으로 표현해도 되겠지만 코딩은 각각 구분짓고 경계를 나누어 코딩하는게 시각적 가독성이 좋습니다. 그래서 일부로 이건 음길이 계산이다라고 별도로 한줄로 표현한 것이죠.

나머지 코딩부분은 이전 시간의 코딩과 동일합니다. 다른점은 멜로디 음계와 음길이를 별도로 배열변수에 저장했다는 것이죠.

5. 결과

실제로 회로도 구성이 쉬워서

라즈베리파에 설치한 아두이노 IDE 을 통해 바로 아두이노에 프로그램을 이식했습니다.


문제는 동영상 촬영했는데 소리는 원래 크게 들렸는데 녹화시 너무 작게 소리가 들어가서 실제 실험동영상은 올리는 것을 포기 했고 사진만 올립니다.

마무리


사실 tone(), noTone(), delay() 세함수를 제어하는 것일 뿐 별다른게 없습니다. 물론 음길이 공식이 1000/noteDurations와 Durations*1.3 으로 딜레이 시간을 준 공식을 추가된 것은 있지만 함수는 3개뿐이 사용 안했고 응용도 저 세개 함수만 사용한 실험이였습니다.

단지 멜로디를 만들기 위해서 배열변수로 코딩량만 늘어났을 뿐이죠. 코딩량은 늘어났지만 사실 멜로리 코딩은 위 악보에서 음계와 음길이를 추출하는 것은 쉽기 때문에 거기서 실제 배열변수로 만드는것은 어렵지 않게 2번 내용을 보시면 하실 수 있을거라고 생각됩니다.

예전에 자전거 멜로디를 만들어서 실험했지만 최근 음악 관련 포스팅을 할때 곰세마리악보를 이용해서 이번 아두이노에서도 곰세마리악보를 이용해 멜로디를 만들어 봤네요.

한번 다른 악보를 보고 멜로디를 만들어보세요.


댓글()