BASIC4MCU | 질문게시판 | atmega328p, CodevisionAVR을 이용한 MPU6050 값 받기 재공부 후 다시 질문드립니다
페이지 정보
작성자 수명양말 작성일2022-05-06 17:48 조회137회 댓글5건본문
안녕하세요, 지난 번에 atmega328p(아두이노 우노 보드), CodevisionAVR을 이용한 MPU6050 값 받기에 대해 질문드렸었는데, 다시 공부해보라는 말씀을 듣고 다시 공부해보았습니다.
(지난 질문 : https://www.basic4mcu.com/bbs/board.php?bo_table=gac&wr_id=19826 )
그 결과, 이론적으로는 맞는 것 같은데 제 코드에서 타이머/카운터0 인터럽트가 동작하지 않는 것 같다는 사실을 알았습니다.
CodeVisionAVR이 제공하는 마법사 기능을 이용해 만든 타이머/카운터 인터럽트 코드를 똑같이 붙여넣었는데도 타이머/카운터 문만 실행하면 동작이 잘 되었으나 제 코드에 붙여넣으면 변수 millis가 증가하지 않는다는 것을 알았습니다.
혹시 무엇이 문제인지 알 수 있을까요?
전체 코드를 올려드리겠습니다.
#include <mega328p.h>
#include <delay.h>
#include <stdio.h>
#include <math.h>
#define FIRST_ADC_INPUT 0
#define ADC_VREF_TYPE 0x00;
/***************************전역변수***********************************/
typedef unsigned char byte;
int millis=0;
int AcX, AcY, AcZ, GyX, GyY, GyZ; // 가속도(Acceleration)와 자이로(Gyro)
float angleAcX, angleAcY;
float angleGyX, angleGyY, angleGyZ;
float angleFiX, angleFiY, angleFiZ;
const float RADIAN_TO_DEGREE = 180 / 3.14159;
const float DEG_PER_SEC = 32767 / 250; // 1초에 회전하는 각도
const float ALPHA = 1 / (1 + 0.04);
// GyX, GyY, GyZ 값의 범위 : -32768 ~ +32767 (16비트 정수범위)
unsigned long now = 0; // 현재 시간 저장용 변수
unsigned long past = 0; // 이전 시간 저장용 변수
float dt = 0; // 한 사이클 동안 걸린 시간 변수
float averAcX, averAcY, averAcZ;
float averGyX, averGyY, averGyZ;
/**************************함수**********************************/
//타이머 인터럽트
interrupt [TIM0_OVF] void timer0_ovf_isr(void){
millis++;
TCNT0=0x06;}//초기값 설정
//UART통신
void U0_TX(char c) { while(!(UCSR0A&0x20)); UDR0=c; }
void U0_STR(char *s) { while(*s)U0_TX(*s++); }
//자이로/가속도센서
void MPU6050_write(byte addr, byte dat){
TWCR = 0xa4;//Start
while (((TWCR & 0x80) == 0x00 || ((TWSR & 0xf8) != 0x08)));
TWDR = 0xd0;//AD+W MPU6050 센서의 Address
TWCR = 0x84;
while (((TWCR & 0x80) == 0x00 || ((TWSR & 0xf8) != 0x18)));
TWDR = addr; // RA
TWCR = 0x84;
while (((TWCR & 0x80) == 0x00 || ((TWSR & 0xf8) != 0x28)));
TWDR = dat;
TWCR = 0x84;
while (((TWCR & 0x80) == 0x00 || ((TWSR & 0xf8) != 0x28)));
TWCR = 0x94;
delay_us(50);
}
byte MPU6050_read(byte addr){
byte dat;
TWCR=0xa4;//Start
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x08)));
TWDR=0xd0;//AD+W
TWCR=0x84;
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x18)));
TWDR=addr;//RA
TWCR=0x84;
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x28)));
TWCR=0xa4;//restart (S)
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x10)));
TWDR=0xd1;// AD+R
TWCR=0x84;
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x40)));
TWCR=0x84;
while(((TWCR&0x80)==0x00||((TWSR&0xf8)!=0x58))); //NO ACK
dat=TWDR;
TWCR=0x94;
return dat;
}
void getData(){
AcX=(int)MPU6050_read(0x3B)<<8|(int)MPU6050_read(0x3C);
AcY=(int)MPU6050_read(0x3D)<<8|(int)MPU6050_read(0x3E);
AcZ=(int)MPU6050_read(0x3F)<<8|(int)MPU6050_read(0x40);
GyX=(int)MPU6050_read(0x43)<<8|(int)MPU6050_read(0x44);
GyY=(int)MPU6050_read(0x45)<<8|(int)MPU6050_read(0x46);
GyZ=(int)MPU6050_read(0x47)<<8|(int)MPU6050_read(0x48);
}
void getDT() {
now=millis;
dt=(now-past)/1000.0;
past=now;
}
void caliSensor() {
int i;
float sumAcX = 0 , sumAcY = 0, sumAcZ = 0;
float sumGyX = 0 , sumGyY = 0, sumGyZ = 0;
getData();
for (i=0;i<10;i++) {
getData();
sumAcX+=AcX; sumAcY+=AcY; sumAcZ+=AcZ;
sumGyX+=GyX; sumGyY+=GyY; sumGyZ+=GyZ;
delay_ms(50);
}
averAcX=sumAcX/10;
averAcY=sumAcY/10;
averAcZ=sumAcY/10;
averGyX=sumGyX/10;
averGyY=sumGyY/10;
averGyZ=sumGyZ/10;
}
void main(void){
float angleTmpX,angleTmpY,angleTmpZ;
char str[100];
int ad0;
/*
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=(1<<CLKPCE);
CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (0<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0);
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif
*/
ADCSRA=0xE7;
TWSR=0x00;
TWBR=0x12;
// Timer/Counter 0 initialization
TCCR0A=0x00;
TCCR0B=0x03; // 250kHz
TCNT0=0x06; //256-250(1ms) = 6
OCR0A=0x00;
OCR0B=0x00;
// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=(0<<OCIE0B) | (0<<OCIE0A) | (1<<TOIE0);
// USART initialization
// USART Baud Rate: 9600
UCSR0A=0x00;
UCSR0B=0x18;
UCSR0C=0x06;
UBRR0H=0x00;
UBRR0L=0x67;
MPU6050_write(0x6b,0x00);
MPU6050_write(0x6c,0x00); //MPU 6050 센서 ON
while(1) {getchar(); break;}
caliSensor();// 초기 센서 캘리브레이션 함수 호출
past = millis;
while (1){
getData();
getDT();
getData();
getDT();
angleAcX = atan(AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2)));
angleAcX *= RADIAN_TO_DEGREE;
angleAcY = atan(-AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2)));
angleAcY *= RADIAN_TO_DEGREE;
// 가속도 센서로는 Z축 회전각 계산 불가함.
// 가속도 현재 값에서 초기평균값을 빼서 센서값에 대한 보정
angleGyX += ((GyX - averGyX) / DEG_PER_SEC) * dt; //각속도로 변환
angleGyY += ((GyY - averGyY) / DEG_PER_SEC) * dt;
angleGyZ += ((GyZ - averGyZ) / DEG_PER_SEC) * dt;
// 상보필터 처리를 위한 임시각도 저장
angleTmpX = angleFiX + angleGyX * dt;
angleTmpY = angleFiY + angleGyY * dt;
angleTmpZ = angleFiZ + angleGyZ * dt;
// (상보필터 값 처리) 임시 각도에 0.96가속도 센서로 얻어진 각도 0.04의 비중을 두어 현재 각도를 구함.
angleFiX = ALPHA * angleTmpX + (1.0 - ALPHA) * angleAcX;
angleFiY = ALPHA * angleTmpY + (1.0 - ALPHA) * angleAcY;
angleFiZ = angleGyZ; // Z축은 자이로 센서만을 이용하여 구함.
if(angleFiX<2 && angleFiX>-2){
angleFiX=0;}
if(angleFiY<2 && angleFiY>-2 ){
angleFiY=0;}
if(angleFiZ<2 && angleFiZ>-2) {
angleFiZ=0;}
ADMUX=0x40; delay_ms(1); ad0=ADCW;
sprintf(str,"000 %3d %3d %3d %3d \n", ad0/5, (int)angleFiX+100, (int)angleFiY+100, (int)angleFiZ+100 ); //5로 나눈 이유 : 아두이노 변동값 번위가 0~200
//sprintf(str, "%3d\n", ad0/5);
U0_STR(str);
delay_ms(50);
}
}
댓글 5
조회수 137수명양말님의 댓글
수명양말 작성일ADMUX는 압력 센서때문에 사용한 것입니다 MPU6050은 I2C 통신했습니다!!
master님의 댓글
master 작성일
void main(void){
float angleTmpX,angleTmpY,angleTmpZ;
char str[100];
int ad0;
ADCSRA=0xE7;
TWSR=0x00;
TWBR=0x12;
// Timer/Counter 0 initialization
TCCR0B=0x03; TCNT0=0x06; TIMSK0=(1<<TOIE0); // 250kHz //256-250(1ms)=6
SREG|=0x80; // 전역인터럽트 활성화 SEI 명령과 동일 <----------------------------------------------------------
// USART Baud Rate: 9600
UCSR0B=0x18; UBRR0L=0x67;
//
MPU6050_write(0x6b,0x00);
MPU6050_write(0x6c,0x00); //MPU 6050 센서 ON
//
while(1){ getchar(); break; }
//
caliSensor();// 초기 센서 캘리브레이션 함수 호출
past=millis;
while(1){
전역인터럽트(화살표 위치) 활성화 시켜야지 인터럽트가 걸리게 됩니다.
전역인터럽트 디저블 상태에서는 어떤 인터럽트도 실행되지 않죠
수명양말님의 댓글
수명양말 작성일
답변 감사합니다!! 덕분에 문제를 찾았습니다.
여전히 MPU6050은 제가 원하는 값이 잘 나오지 않지만 그것은 더 공부해보고 다시 질문드리겠습니다 감사합니다.
master님의 댓글
master
MPU6050 센서 값을 읽을 때 정해진 주기로 읽어야 합니다.
예를들면 50ms마다 센서를 읽으려면
char flag=0; //전역변수 추가
//타이머 인터럽트
interrupt [TIM0_OVF] void timer0_ovf_isr(void){
static char t=0;
TCNT0=0x06; //초기값 설정
millis++;
if(++t>=50){ t=0; flag=1; } // 50ms마다 flag=1
}
메인함수 무한루프에서는
while(1){
if(flag){ flag=0;
여기서 6050 센서값을 읽어와서 처리 함
}
}
}
수명양말님의 댓글
수명양말
그렇군요 답변 정말 감사합니다 좋은 하루 되세요.