BASIC4MCU | 질문게시판 | AVR 자이로센서사용
페이지 정보
작성자 하하루다 작성일2018-01-16 19:41 조회20,175회 댓글5건본문
자이로센서를 사용해서 자이로 XY 가속도XY의 값을 CLCD에 나타내는데 목표를 두고 공부하고 있는 학생입니다. MCU는 ATMGEGA128을 사용하려고 합니다.
책과 구글링을 통해 소스를 만들어가는중 타이머를 이용해 CLCD에 값을 뛰워보려는데 어떻게 해야할지 몰라 글을 남깁니다.
#include <util/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h> //인터럽트 헤더 파일
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define F_CPU 16000000UL
#define LCD_WDATA PORTA //LCD데이터 포트 정의
#define LCD_WINST PORTA //
#define LCD_CTRL PORTE //LCD 제어 포트 정의
#define LCD_EN 0 //LCD 제어(PING0~2)를 효과적으로 하기위한 정의
#define LCD_RW 1
#define LCD_RS 2
#define Byte unsigned char //자주쓰이는 무부호 문자형 자료형을 Byte로 선언
typedef unsigned char byte;
/*------MPU------------*/
byte MPU6050_read(byte addr);
void MPU6050_write(byte addr, char data);
void getRawData();
unsigned int gx = 0, gy = 0, gz = 0, ax = 0, ay = 0, az = 0;
volatile long x_aTmp1, y_aTmp1, z_aTmp1;
volatile float x_aTmp2, y_aTmp2, z_aTmp2, x_aResult, y_aResult, z_aResult;
volatile float x_gTmp1, y_gTmp1, x_gResult, y_gResult;
byte buffer[12];
int a=0, b=0;
void LCD_Data(Byte ch)
{
LCD_CTRL |= (1 << LCD_RS); //RS=1, R/W=0 으로 데이터 쓰기 싸이클
LCD_CTRL &= ~(1 << LCD_RW);
LCD_CTRL |= (1 << LCD_EN); //LCD 사용
_delay_us(50);
LCD_WDATA = ch; //데이터 출력
_delay_us(50);
LCD_CTRL &= ~(1 << LCD_EN); //LCD 사용안함
}
// 0 1
void LCD_Comm(Byte ch) //PG0-EN , PG1-RW, PG2-RS , PG4-TOSC1핀(사용안함)
{ //LCD_CTRL = LCD제어부 포트(4핀인데 실질적인 사용3핀)
LCD_CTRL &= ~(1 << LCD_RS); // RS=0, RW=0 으로 정의함.
LCD_CTRL &= ~(1 << LCD_RW);
LCD_CTRL |= (1 << LCD_EN); //LCD 사용허가
_delay_us(50);
LCD_WINST = ch; //명령어 쓰기
_delay_us(50);
LCD_CTRL &= ~(1 << LCD_EN); //LCD 사용안함
}
void LCD_CHAR(Byte c) //한문자 출력 함수
{
LCD_Data(c); //CGROM 문자코드의 0x31 ~ 0xFF 는 아스키코드와 일치함!
_delay_ms(1);
}
void LCD_STR(Byte *str) //↑문자열을 한문자씩 출력함수로 전달
{
while(*str !=0)
{
LCD_CHAR(*str);
str++;
}
}
void LCD_pos(unsigned char col, unsigned char row) //LCD 포지션 설정
{
LCD_Comm(0x80 | (row+col*0x40)); // row=행 / col=열 ,DDRAM주소설정
}
void LCD_Clear(void) // 화면 클리어
{
LCD_Comm(0x01);
_delay_ms(2); //1.6ms이상의 실행시간소요로 딜레이필요
}
void LCD_Init(void) //LCD 초기화
{
LCD_Comm(0x38); //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)
_delay_ms(2);
LCD_Comm(0x38); //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)
_delay_ms(2);
LCD_Comm(0x38); //데이터 8비트 사용, 5X7도트 , LCD2열로 사용(6)
_delay_ms(2);
LCD_Comm(0x0e); //Display ON/OPFF
_delay_ms(2);
LCD_Comm(0x06); //주소 +1 , 커서를 우측으로 이동 (3)
_delay_ms(2);
LCD_Clear();
}
int main()
{
int unsigned adc_data;
float volt, femp;
char str[50];
TIMSK = 0x01;
TCCR0 = 0x07;
TCNT0 = 99;
SREG = 0x80;
DDRA = 0xff; //PORTA(LCD연결포트)를 출력으로 설정
DDRE = 0x0f; //PORTE(LCD컨트롤)의 하위 4비트를 출력으로설
LCD_Clear;
LCD_Init();
TWSR = 0x00;
TWBR = 0x12;
MPU6050_write(0x6B, 0x00);
MPU6050_write(0x6C, 0x00);
getRawData();
while(1)
{
getRawData();
//자이로값 출력
LCD_pos(0,0); sprintf(str,"GYRO x=%4d",x_gResult); LCD_STR(str);
LCD_pos(1,0); sprintf(str," y=%4d ", y_gResult); LCD_STR(str);
_delay_ms(500);
}
}
byte MPU6050_read(byte addr)
{
byte dat;
TWCR = 0xA4;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x08)));
TWDR = 0xD0;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x18)));
TWDR = addr;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
TWCR = 0xA4;
//-------------------------------------------------------------
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x10)));
TWDR = 0xD1;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x40)));
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x58)));
dat = TWDR;
TWCR = 0x94;
return dat;
}
void MPU6050_write(byte addr, char data)
{
TWCR = 0xA4;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x08)));
TWDR = 0xD0;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x18)));
TWDR = addr; // addr = 0x43
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
//-------------------------------------------------------------
TWDR = data;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
TWCR = 0x94;
_delay_us(50);
}
interrupt [TIM0_OVF] void timer_int0(void) { getRawData(); getAcclDegree(); getGyroDegree(); compFilter(); TCNT0 = 99; }
void getRawData()
{
buffer[0] = MPU6050_read(0x3B);
buffer[1] = MPU6050_read(0x3C);
buffer[2] = MPU6050_read(0x3D);
buffer[3] = MPU6050_read(0x3E);
buffer[4] = MPU6050_read(0x3F);
buffer[5] = MPU6050_read(0x40);
buffer[6] = MPU6050_read(0x43);
buffer[7] = MPU6050_read(0x44);
buffer[8] = MPU6050_read(0x45);
buffer[9] = MPU6050_read(0x46);
buffer[10] = MPU6050_read(0x47);
buffer[11] = MPU6050_read(0x48);
ax = (int)buffer[0] << 8 | (int)buffer[1];
ay = (int)buffer[2] << 8 | (int)buffer[3];
az = (int)buffer[4] << 8 | (int)buffer[5];
gx = (int)buffer[6] << 8 | (int)buffer[7];
gy = (int)buffer[8] << 8 | (int)buffer[9];
gz = (int)buffer[10] << 8 | (int)buffer[11];
}
void getAcclDegree(void)
{
x_aTmp1 = ((long)ay * (long)ay) + ((long)az * (long)az);
y_aTmp1 = ((long)ax * (long)ax) + ((long)az * (long)az);
z_aTmp1 = ((long)ay * (long)ay) + ((long)az * (long)az);
x_aTmp2 = sqrt((float)x_aTmp1);
y_aTmp2 = sqrt((float)y_aTmp1);
z_aTmp2 = sqrt((float)z_aTmp1);
x_aResult = atan((float)ax / x_aTmp2);
y_aResult = atan((float)ay / y_aTmp2);
z_aResult = atan(z_aTmp2 / (float)az);
}
void getGyroDegree(void)
{
x_gTmp1 = (float)gx / 65536;
y_gTmp1 = (float)gy / 65536;
x_gTmp1 = x_gTmp1 * 1.8;
y_gTmp1 = y_gTmp1 * 1.8;
x_gResult = x_gTmp1;
y_gResult = y_gTmp1;
}
이렇게 옮겨 보았습니다. 조언 부탁드립니다.
댓글 5
조회수 20,175master님의 댓글
master 작성일
현재는 메인의 무한루프 안에서 LCD 출력을 하고 있습니다.
타이머를 이용해서 LCD를 출력하고 싶다고 적었는데
무슨 뜻인지 모르겠습니다.
dhksdlf218님의 댓글
dhksdlf218
#include <util/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h> //인터럽트 헤더 파일
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned char byte;
/*------MPU------------*/
byte MPU6050_read(byte addr);
void MPU6050_write(byte addr, char data);
void getRawData();
unsigned int gx = 0, gy = 0, gz = 0, ax = 0, ay = 0, az = 0;
byte buffer[12];
void main(void)
{
TWSR = 0x00;
TWBR = 0x12;
MPU6050_write(0x6B, 0x00);
MPU6050_write(0x6C, 0x00);
getRawData();
while(1)
{
getRawData();
}
}
byte MPU6050_read(byte addr)
{
byte dat;
TWCR = 0xA4;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x08)));
TWDR = 0xD0;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x18)));
TWDR = addr;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
TWCR = 0xA4;
//-------------------------------------------------------------
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x10)));
TWDR = 0xD1;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x40)));
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x58)));
dat = TWDR;
TWCR = 0x94;
return dat;
}
void MPU6050_write(byte addr, char data)
{
TWCR = 0xA4;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x08)));
TWDR = 0xD0;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x18)));
TWDR = addr; // addr = 0x43
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
//-------------------------------------------------------------
TWDR = data;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00 || ((TWSR & 0xF8) != 0x28)));
TWCR = 0x94;
_delay_us(50);
}
void getRawData()
{
buffer[0] = MPU6050_read(0x3B);
buffer[1] = MPU6050_read(0x3C);
buffer[2] = MPU6050_read(0x3D);
buffer[3] = MPU6050_read(0x3E);
buffer[4] = MPU6050_read(0x3F);
buffer[5] = MPU6050_read(0x40);
buffer[6] = MPU6050_read(0x43);
buffer[7] = MPU6050_read(0x44);
buffer[8] = MPU6050_read(0x45);
buffer[9] = MPU6050_read(0x46);
buffer[10] = MPU6050_read(0x47);
buffer[11] = MPU6050_read(0x48);
ax = (int)buffer[0] << 8 | (int)buffer[1];
ay = (int)buffer[2] << 8 | (int)buffer[3];
az = (int)buffer[4] << 8 | (int)buffer[5];
gx = (int)buffer[6] << 8 | (int)buffer[7];
gy = (int)buffer[8] << 8 | (int)buffer[9];
gz = (int)buffer[10] << 8 | (int)buffer[11];
}
clcd에 뛰오고 싶었지만 master님이 말씀하시는 말이 정확히 이해 못해서 바로 tera term같은 프로그램으로 값을 읽으려고 합니다. 어떤 프로그램을 써야할지 몰겟습니다. 조언 부탁드립니다.
master님의 댓글
master
테라텀이 아니라도 시리얼 통신 모니터링 프로그램은 널려있습니다.
아두이노 시리얼모니터 창도 상관없고
코드비젼의 터미널창으로도 가능하죠
테라텀으로도 문제 안됩니다.
윈도우의 하이퍼터미널은 가장 기본적인 프로그램인데
높은 버전의 윈도우에서 지원이 안되면 낮은 버전의 하이퍼터미널을 사용 할 수도 있을겁니다.
master님의 댓글
master 작성일
타이머0 인터럽트 주기는 약 10ms로 설정 했지요?
LCD_pos(0,0); sprintf(str,"GYRO x=%4d",x_gResult); LCD_STR(str);
LCD_pos(1,0); sprintf(str," y=%4d ", y_gResult); LCD_STR(str);
대략 26문자 정도를 출력하고 있는데
LCD 데이터 출력함수의 딜레이는 1ms이고
컴맨드 출력함수의 딜레이는 2ms입니다.
26개의 데이터와 2개의 컴맨드가 있어야하니
LCD 출력에는 대략 30ms 이상이 걸립니다.
10ms 주기의 인터럽트에서 30ms이상의 코드를 돌리면 메인은 전혀 동작하지 않게 되며
센서를 읽는데에는 정확한 주기로 실행 해야하는데
인터럽트 함수를 실행 하고나서 여유시간이 없어서 측정 주기가 정확하게 맞지 않게 됩니다.
master님의 댓글
master 작성일
현재 질문 소스의 내용 중
LCD 함수의 처리 속도를 빠르게 변경하려면
데이터 출력함수는 50us + 50us로 되어 있는데 -> 1us + 40us로 변경 할 수 있습니다.
LCD_pos() 함수에서 사용하는 컴맨드 출력 함수 경우에도 -> 1us + 40us로 변경 할 수 있습니다.
함수의 실행 시간이 약 50us로 줄어들게 되면 LCD 출력의 전체 시간이 1.5ms로 대폭 줄어들게 됩니다.
물론 LCD 모델에 따라서 약간의 차이가 있을 수 있습니다.
어떤 LCD는 출력하고나서 일정시간의 딜레이를 요구하기도 합니다.