2015년 5월 25일 월요일
ATmega128A에서 light_ws2812 라이브러리로 WS2812B 사용하기
github애서 light_ws2812를 다운받으면 light_apa102_AVR, light_ws2812_Arduino, light_ws2812_ARM, light_ws2812_AVR 디렉토리가 있고 리눅스나 *nix 환경에서 Makefile을 사용하는데 익숙한 사람이 아니면 막상 라이브러리를 받아도 사용하는데 힘들수가 있다.
윈도우 환경에서 AVR 프로그래밍을 하는 경우 가장 일반적인 경우 WinAVR과 AVRStudio4를 많이 사용하므로 그 기준으로 설명하겠다.
라이브러리에 필요한 파일은 light_ws2812_AVR 디렉토리 내의 ws2812_config.h, Light_WS2812/light_ws2812.c, Light_WS2812/light_ws2812.h 이다. 자신의 프로젝트 디렉토리에 이 3개의 파일을 복사한 후, 프로젝트의 소스파일과 헤더파일에 각각 추가시켜줘야 한다.
그리고 기본 설정은 WS2812의 Din 핀은 PortB의 1번번 핀에 연결하도록 되어 있다. 핀 포트/번호를 변경하려면 ws2812_config.h 파일의 아래 부분을 변경해 주면 된다.
또한 예제 파일을 실행해 보고 싶으면 Examples 디렉토리에 Chained_writes.c, RGB_blinky.c 가 들어있어 이 파일들을 라이브러리와 함께 빌드 해 주면 된다. 단 예제파일은 ATtiny85 기준으로 작성되어 있으므로 ATmega128A에서 사용하려면 아래 소스코드에서 빨간색으로 표시한 부분은 삭제하고 빌드하면 된다.
라이브러리를 사용하는 방법은 예제 코드를 보면 쉽게 알 수 있다. 각 WS2812 LED는 r,g,b 값을 가지고 ws2812_sendarray() 함수를 이용해 각 LED의 rgb 값이 들어있는 포인터와 전송할 바이트 수를 넘겨주면 곧바로 LED에 color가 반영된다. (각 LED당 3바이트가 되므로 8개 LED의 color를 변경하려면 8*3 = 24 바이트를 전송해 줘야 한다.)
전송이 시작되면 처음 3바이트는 첫번째 LED의 color, 다음 3바이트는 두번째 LED의 color...이런식으로 색이 변경된다. 전송이 끝나고 다시 첫번째 LED부터 색을 변경하려면 50us 딜레이를 줘야 한다.
위의 그림처럼 ws2812_sendarray() 함수를 연속으로 호출하면 다음 LED의 color가 변경된다.
위의 그림처럼 ws2812_sendarray 함수를 호출하기 전에 50us 이상의 딜레이가 있으면 다음번 ws2812_sendarray 함수 호출로 보내는 데이터가 다시 첫번째 LED color부터 변경된다.
2009년 4월 24일 금요일
Arduino에 카드리더기 연결 (Use magnetic card reader with Arduino)
Arduino에 Magtek의 magnetic strip reader를 연결하여 보았다.
현재 가지고 있는 리더기는 TTL 인터페이스를 가지고 있는 single track reader이다. 5핀 커넥터를 가지고 있고 Vcc, GND, Card present, Clock(Strobe), Data 신호를 제공해 준다.
신호 타이밍은 아래와 같다.
Arduino의 D2, D3, D5를 각각 리더기의 Data, Clock, Card present에 연결해 주면 된다.
코드는 다음과 같다.
View source code
* Magnetic Stripe Reader
* by Stephan King http://www.kingsdesign.com
*
* Reads a magnetic stripe.
*
*/
int cld1Pin = 5; // Card status pin
int rdtPin = 2; // Data pin
int reading = 0; // Reading status
volatile int buffer[400]; // Buffer for data
volatile int i = 0; // Buffer counter
volatile int bit = 0; // global bita
char cardData[40]; // holds card info
int charCount = 0; // counter for info
int DEBUG = 0;
void setup() {
Serial.begin(38400);
// The interrupts are key to reliable
// reading of the clock and data feed
attachInterrupt(0, changeBit, CHANGE);
attachInterrupt(1, writeBit, FALLING);
}
void loop(){
// Active when card present
while(digitalRead(cld1Pin) == LOW){
reading = 1;
}
// Active when read is complete
// Reset the buffer
if(reading == 1) {
if (DEBUG == 1) {
printBuffer();
}
decode();
reading = 0;
i = 0;
int l;
for (l = 0; l < 40; l = l + 1) {
cardData[l] = '\n';
}
charCount = 0;
}
}
// Flips the global bit
void changeBit(){
if (bit == 0) {
bit = 1;
} else {
bit = 0;
}
}
// Writes the bit to the buffer
void writeBit(){
buffer[i] = bit;
i++;
}
// prints the buffer
void printBuffer(){
int j;
for (j = 0; j < 200; j = j + 1) {
Serial.println(buffer[j]);
}
}
int getStartSentinal(){
int j;
int queue[5];
int sentinal = 0;
for (j = 0; j < 400; j = j + 1) {
queue[4] = queue[3];
queue[3] = queue[2];
queue[2] = queue[1];
queue[1] = queue[0];
queue[0] = buffer[j];
if (DEBUG == 1) {
Serial.print(queue[0]);
Serial.print(queue[1]);
Serial.print(queue[2]);
Serial.print(queue[3]);
Serial.println(queue[4]);
}
if (queue[0] == 0 & queue[1] == 1 & queue[2] == 0 & queue[3] == 1 & queue[4] == 1) {
sentinal = j - 4;
break;
}
}
if (DEBUG == 1) {
Serial.print("sentinal:");
Serial.println(sentinal);
Serial.println("");
}
return sentinal;
}
void decode() {
int sentinal = getStartSentinal();
int j;
int i = 0;
int k = 0;
int thisByte[5];
for (j = sentinal; j < 400 - sentinal; j = j + 1) {
thisByte[i] = buffer[j];
i++;
if (i % 5 == 0) {
i = 0;
if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0) {
break;
}
printMyByte(thisByte);
}
}
Serial.print("Stripe_Data:");
for (k = 0; k < charCount; k = k + 1) {
Serial.print(cardData[k]);
}
Serial.println("");
}
void printMyByte(int thisByte[]) {
int i;
for (i = 0; i < 5; i = i + 1) {
if (DEBUG == 1) {
Serial.print(thisByte[i]);
}
}
if (DEBUG == 1) {
Serial.print("\t");
Serial.print(decodeByte(thisByte));
Serial.println("");
}
cardData[charCount] = decodeByte(thisByte);
charCount ++;
}
char decodeByte(int thisByte[]) {
if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 1){
return '0';
}
if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0){
return '1';
}
if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 0){
return '2';
}
if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 0 & thisByte[4] == 1){
return '3';
}
if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 0){
return '4';
}
if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 1){
return '5';
}
if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 1){
return '6';
}
if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 0 & thisByte[4] == 0){
return '7';
}
if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 0){
return '8';
}
if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 1){
return '9';
}
if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 1){
return ':';
}
if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 0 & thisByte[3] == 1 & thisByte[4] == 0){
return ';';
}
if (thisByte[0] == 0 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 1){
return '<';
}
if (thisByte[0] == 1 & thisByte[1] == 0 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 0){
return '=';
}
if (thisByte[0] == 0 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 0){
return '>';
}
if (thisByte[0] == 1 & thisByte[1] == 1 & thisByte[2] == 1 & thisByte[3] == 1 & thisByte[4] == 1){
return '?';
}
}
카드를 리더기에 긁어주면 아래와 같이 카드의 데이터가 시리얼포트로 출력된다.
문제점은 위의 코드로는 디코딩 루틴이 카드를 정방향인 경우에만 동작하고 반대 방향으로 긁어주는 경우에는 데이터를 정상적으로 디코딩 해 주지 못한다. 역방향인 경우도 정상적으로 디코딩이 가능하도록 코드를 수정 할 예정이다.
2009년 4월 23일 목요일
PD2437을 Arduino에서 사용하기 (Connect PD2437 (5x7 character 4-digit display) to Arduino)
동네의 부품샵을 구경하다 우연히 구하게 된 부품이다. 저렴한 가격($1.99)때문에 메뉴얼 여부에 관계 없이 일단 구입하여 버렸다. 물론 '구글신'을 믿고 있었기 때문이지만...
부품 명칭은 PD2437로 OSRAM에서 나온 4-character 5x7 dot matrix alphanumeric programmable display with built-in CMOS control functions 이다. DIL 패키지 안에 5x7 매트릭스가 4개 들어있고 디스플레이 컨트롤러를 내장하고 있다.
이 부품을 사용하기 위해 구글의 도움으로 유저메뉴얼과 어플리케이션 노트를 찾아내었다.
일반적인 메모리같이 8-bit bidirectional data bus를 사용해서 마이크로컨트롤러와 데이터를 교환하고 ASCII 코드를 입력받아 그에 해당하는 문자를 표시하게 되어 있었다.
Arduino에 연결하려고 하는데 8-bit data bus를 모두 digital i/o에 연결해 버리면 arduino에 다른 주변기기를 붙일 수 없을거 같아 74LS164(8-Bit Parallel-Out Serial-In Shift Registers)를 같이 사용하기로 하였다.
이를 토대로 구성한 회로는 다음과 같다.
Arduino의 D2-D9까지를 사용하였다.
실제 기판에 만들기 전에 먼저 브레드보드에서 프로토타입으로 확인을 해 보았다.
큰 문제 없이 원하는 글자를 표시해 주었다. 테스트에 사용한 코드는 다음과 같다.
View source code
* PD2437 Test
*/
/*
- Addressing
0xx : control word
100 : digit 0 (rightmost)
101 : digit 1
110 : digit 2
111 : digit 3 (leftmost)
- Control Word
b7 : clear
0 standard operation
1 clear entire display
b6 : lamp test
0 standard operation
1 display all dots at 50% brightness
b5 : blink
0 blink attribute disabled
1 blink entire display
b4 : attribute enable
0 disable above attributes
1 enable above attributes
b3,b2: attributes
00 display cursor instead of character
01 blink character
10 display blinking cursor instead of character
11 alternate character with cursor
b1,b0: brightness
00 0% (black)
01 25%
10 50%
11 100%
-----------------------------------------------------
how to load information into the display
SET BRIGHTNESS
1. set brightness level of the entire display to your preference, (000) <- 0x02 (50%)
LOAD FOUR CHARACTERS
2. load "S" in the left hand digit, (111) <- 'S'
3. load "T" in the next digit, (110) <- 'T'
4. load "O" in the next digit, (101) <- 'O'
5. load "P" in the right hand digit, (100) <- 'P'
BLINK A SINGLE CHARACTER
6. into the digit, second from the right, load the hex code 0xcf, which is the code for an 'O' with the d7 bit added as a control bit, (101) <- 0xcf
7. load enabled blinking character into the control word register. the display should show "STOP" with a flashing "O", (000) <- 0x17
ADD ANOTHER BLINKING CHARACTER
8. into the left hand digit, load the he code 0xd3 which gives an 'S' with d7 bit added as a control bit, (111) <- 0xd3
ALTERNATE CHARACTER/CURSOR ENABLE
9. load enable alternate character/cursor into the control word register. the display now should show "STOP" with the 'O' and the 'S' alternating between the letter and cursor. (000) <- 1f
INITIATE FOUR CHARACTER BLINKING
10. load enable display blinking. the display now should show the entire word "STOP" blinking, (000) <- 0x23
*/
// Connected to LS164
int DATA = 2; // LS164 Din (pin 1&2)
int CLK = 3; // LS164 Clk (pin 8)
// Connected to PD2437
int A0 = 6; // Address 0 (pin 7)
int A1 = 5; // Address 1 (pin 8)
int A2 = 4; // Address 2 (pin 9)
int WR = 7; // Write (pin 11)
int CE = 8; // Chip Enable (NEG, pin 6)
int RST = 9; // Reset (NEG, pin 4)
void setup() // run once, when the sketch starts
{
Serial.begin(38400);
// pins for LS164
pinMode(DATA, OUTPUT); // sets the digital pin as output
pinMode(CLK, OUTPUT); // sets the digital pin as output
// pins for PD2437
pinMode(A0, OUTPUT);
pinMode(A1, OUTPUT);
pinMode(A2, OUTPUT);
pinMode(WR, OUTPUT);
pinMode(CE, OUTPUT);
pinMode(RST, OUTPUT);
// Reset PD2437
digitalWrite(CE, HIGH);
digitalWrite(WR, HIGH);
digitalWrite(RST, LOW);
digitalWrite(RST, HIGH);
}
void loop() // run over and over again
{
putChar(0, 0x83);
delay(100);
putChar(7,'S');
delay(100);
putChar(6,'T');
delay(100);
putChar(5,'O');
delay(100);
putChar(4,'P');
delay(100);
putChar(4, 'P' | 0x80); // 'P' w/ MSB
delay(100);
putChar(0, 0x17);
delay(100);
putChar(7, 'S' | 0x80); // 'S' w/ MSB
delay(100);
putChar(0, 0x1f);
delay(100);
putChar(0, 0x23);
delay(1000);
}
//
// Subroutine for LS164
//
// Send byte to LS164
void sendByte(int d)
{
shiftOut(DATA, CLK, MSBFIRST, d);
}
//
// Subroutine for PD2437
//
// set address for PD2437
void setAddr(int adr)
{
digitalWrite(A0, (adr&0x1));
digitalWrite(A1, (adr&0x2));
digitalWrite(A2, (adr&0x4));
}
void putChar(int pos, int ch)
{
digitalWrite(CE, LOW);
setAddr(pos);
sendByte(ch);
digitalWrite(WR, LOW);
digitalWrite(WR, HIGH);
digitalWrite(CE, HIGH);
}
PD2437은 매우 쉽게 여러개를 cascading이 가능하기 때문에 현재 총 10개의 PD2437을 연결하여 20x2 LED display를 만들어 볼까 생각중이다. LCD에 비해 직접 발광을 하기 때문에 시야각 문제도 없고 시중에서 구할 수 있는 20x2 LCD보다 크기도 크기 때문에 야간에도 글자를 정확하게 확인할 수 있기 때문에 signboard로 적합할 거 같다.
2009년 4월 18일 토요일
Arduino 설치 및 프로그램 실행 (Installing and setup Arduino)
http://arduino.cc 에서 arduino 소프트웨어를 다운받을 수 있다. Mac OS X, Linux, Windows를 모두 다 지원한다.
또한 OS용 디바이스 드라이버도 이 사이트에서 다운받을 수 있다.
다운받은 압축파일을 풀어주면 프로그램을 가지고 있는 폴더가 생긴다. 별도 설치 할 필요가 없이 그 폴더에서 arduino 프로그램을 바로 실행해주면 된다.
프로그램을 실행하면 다음과 같은 창이 열리게 된다..
먼저 arduino 보드에 맞게 환경설정을 해 주어야 한다. Tools 메뉴로 가 보면 아래와 같은 서브메뉴가 나온다.
먼저 통신에 사용할 시리얼 포트를 지정해 준다. Arduino보드를 USB 케이블에 연결하고 디바이스 드라이버가 제대로 설치되어 있으면 아래와 같이 포트가 보이게 된다. 아래 화면은 맥의 경우이고 윈도우 인 경우 COMx 란 이름으로 보이게 된다. 보드를 연결했는데도 포트가 보이지 않는다면 디바이스 드라이버를 다시 설치하고 프로그램을 실행하면 보일 것이다.
그 다음은 보드를 설정해 준다. Arduino도 여러 종류가 있어서 그에 해당하는 보드를 선택해주면 된다.
여기서는 아래의 보드를 사용할 것이기 때문에 Arduino Diecimila를 선택해주면 된다.
설정이 끝났으면 예제 프로그램을 실행시켜 보겠다. 아래 화면에서처럼 File -> Sketchbook -> Example -> Digital -> Blink 를 선택해 준다.
그러면 Blink 예제 파일이 열리고 내용을 볼 수 있게 된다. 여기서 맨 왼쪽의 플레이버튼을 누르면 프로그램이 컴파일 된다.
컴파일이 정상적으로 끝나면 화면 아래쪽에 다음과 같은 메세지가 나온다.
하지만 컴파일이 끝났어도 아직 프로그램이 arduino 보드로 옮겨진건 아니다. 프로그램을 arduino 보드로 전송하려면 오른쪽 두번째 버튼을 눌러준다.
그러면 arduino 보드의 TX, RX LED가 빠르게 번쩍이면서 프로그램이 전송되는걸 볼 수 있다. 전송이 끝나면 컴파일의 경우와 마찬가지로 메시지 창에 업로드가 끝났다는 메시지가 표시된다.
프로그램이 전송되고 나면 arduino 보드는 자동으로 reset이 되면서 프로그램이 바로 실행된다. 이 예제 프로그램은 보드에 있는 LED를 깜빡이게 하는 것이기 때문에 아래의 LED가 1초 간격으로 반짝이는걸 확인할 수 있을 것이다.
또한 arduino에는 시리얼 터미널 기능도 가지고 있다. 위의 예제에서는 시리얼 포트를 사용하지 않았지만 프로그램에 따라서는 시리얼 포트를 통해 데이터를 주고받을 수 있다. 맨 오른쪽의 아이콘을 눌러주면 아래쪽 메시지 윈도우가 시리얼 터미널 역활을 하게 된다.
검은 부분에 수신한 데이터가 표시되고 송신할 데이터는 입력창에 넣어주면 된다. 또한 통신속도도 임의로 변경할 수 있다. 물론 arduino보드의 시리얼 포트 설정속도와 동일하게 맞춰줘야만 한다.




