Здравствуйте. Имеется задумка сделать цифровой компас на микросхемах HoneyWell.
Три датчика магнитного поля X,Y - hmc1022 и Z-hmc1021Z. В качетсве компенсации угла наклона использовать ADXL213. Точность высокая не нужна поэтому использую встроенный АЦП ATmega16. Результат вывожу на компьютер по rs232. Программу пишу в CodeVision. Датчики пока заказываю. В "Базе Электроники" комплект обошелся в 2200 руб.
Схему делаю по DataSheet-у.
Вот что у меня получилось. Т.к. ADXL213 имеет температурный дрейф,
добавил датчик температуры DS18S20.
Вот с выходом акселлерометра (ШИМ) не могу разобраться.
Для вычесления азимута необходимо измерять угол наклона в двух плоскостях x и y.
Вот ссылка на DataSheet
http://www.ssec.honeywell.com/magnetic/datasheets/hmc1055.pdf
#include <mega16.h>
// 1 Wire Bus functions
#asm
.equ __w1_port=0x18 ;PORTB
.equ __w1_bit=0
#endasm
#include <1wire.h>
// DS1820 Temperature Sensor functions
#include <ds1820.h>
#define MAX_DEVICES 8
#define Rising 1
#define Falling 0
#include <delay.h>
/* DS1820/DS18S20 devices ROM code storage area */
unsigned char rom_code[MAX_DEVICES][9],CurrentEdge;
unsigned int temp,a1,a2,a3,i,devices,T1=0, T2=0, PreviousTime, CurrentTime; // температура, выходы с датчиков соотвественно x,y,z
char accel=0; //угол
// Standard Input/Output functions
#include <stdio.h>
// Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
if (CurrentEdge == Rising){
CurrentTime = ICR1L;
TCCR1B = 0b00000001; // Set Falling edge
CurrentEdge = Falling;
if (CurrentTime>PreviousTime) T2=CurrentTime-PreviousTime;
else T2 = 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
} else { // if Edge Falling
CurrentTime = ICR1L;
TCCR1B = 0b01000001; // set Rising edge
CurrentEdge = Rising;
if (CurrentTime>PreviousTime) T1= CurrentTime-PreviousTime;
else T1= 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
// Place your code here
}
#define ADC_VREF_TYPE 0xC0
void init_avr();
// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | (ADC_VREF_TYPE & 0xff);
// Delay needed for the stabilization of the ADC input voltage
delay_us(10);
// Start the AD conversion
ADCSRA|=0x40;
// Wait for the AD conversion to complete
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10;
return ADCW;
}
// Declare your global variables here
void main(void)
{
init_avr();
devices=w1_search(0xf0,rom_code);
while (1)
{
i=0;
temp=ds1820_temperature_10(&rom_code[0]);
printf("t=%i.%uxf8Cnr",temp/10,temp%10);
PORTC.0=0; //setreset сброс датчиков перед ацп
delay_ms(1);
PORTC.0=1;
a1=read_adc(1);
printf("a1=%d",a1);
putchar(0x0D);
a2=read_adc(2);
printf("a2=%d",a2);
putchar(0x0D);
a3=read_adc(3);
printf("a3=%d",a3);
putchar(0x0D);
accel = accel + (T1/(T1+T2)-0.546)/0.125;
// Place your code here
};
}
void init_avr(void)
{
// Declare your local variables here
// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTA=0x00;
DDRA=0x00;
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x01;
DDRC=0xFF;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
OCR0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 4000,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: On
// Input Capture on Rising Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: On
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0xC1;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x20;
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=0x00;
UCSRB=0x08;
UCSRC="0x86;"
UBRRH=0x00;
UBRRL=0x19;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// ADC initialization
// ADC Clock frequency: 125,000 kHz
// ADC Voltage Reference: Int., cap. on AREF
// ADC Auto Trigger Source: None
ADMUX=ADC_VREF_TYPE & 0xff;
ADCSRA=0x85;
// 1 Wire Bus initialization
w1_init();
// Global enable interrupts
#asm("sei")
}
Здравствуйте, mas_nk!
Работал когда-то с ШИМ на ATmega32, считывал показания датчика ускорения ADXL202, вот тут: https://radioded.ru/index.php?option=com_content&task=view&id=83&Itemid=49 .Как я понял,Вы это видели. А в чем проблема, что именно не работает, вообще никаких данных не можете с ШИМ? Вроде, по коду все ОК, и прерывание включено и т.д. и т.п. Единственное, что может быть, так это, то что Вы используете другой чип, не ATmega32, возможно надо будет писать другие значения в регистры TCCR1B... И как я понял, у вас неправильно настроен таймер1, при создании проекта:
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 4000,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: On
// Input Capture on Rising Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: On
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0xC1;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
Попробуйте писать код без вставок CodeVisionAVR, или возьмите мой исходник под WinAVR.
Radioded, пожалуйста прокоментируйте подробно ваш код, чтобы мне разобраться и можно было настроить таймер и написать код под мой чип.
Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
if (CurrentEdge == Rising){
CurrentTime = ICR1;
TCCR1B = 0b00000001; // Set Falling edge
CurrentEdge = Falling;
if (CurrentTime>PreviousTime) T2=CurrentTime-PreviousTime;
else T2 = 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
} else { // if Edge Falling
CurrentTime = ICR1;
TCCR1B = 0b01000001; // set Rising edge
CurrentEdge = Rising;
if (CurrentTime>PreviousTime) T1= CurrentTime-PreviousTime;
else T1= 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
// Place your code here
Вот добавил комментарии, они напечатаны жирным шрифтом:
Timer 1 input capture interrupt service routine
interrupt [TIM1_CAPT] void timer1_capt_isr(void) // -- прерывание по изменению уровня на ножке Input Capture Pin
{
if (CurrentEdge == Rising){ // -- этот кусок кода выполняется, если прерывание было по нарастающему фронту, переменнаю CurrentEdge - является для нас своеобразным флагом, показывающим по какому фронту произошло прерывание.
CurrentTime = ICR1; // -- считываем в переменную CurrentTime время возникновения нарастающего фронта
TCCR1B = 0b00000001; // Set Falling edge // -- устанавливаем прерывание по спадающему фронту чтобы измерить длительность ШИМ импульса
CurrentEdge = Falling; // -- устанавливаем флаг (для себя), что следующий захват по ниспадающему фронту
if (CurrentTime>PreviousTime) T2=CurrentTime-PreviousTime; // -- В этих строках идет вычисление длительности ШИМ "1" в тактах МК
else T2 = 65535-PreviousTime+CurrentTime; // -- считаем так, если значение таймера перевалило за значение 65535 т.е. пошел считать с нуля
PreviousTime=CurrentTime;
} else { // if Edge Falling //-- этот кусок кода будет выполнятся, если прерывание было по ниспадающему фронту, далее всё аналогично, как было выше
CurrentTime = ICR1;
TCCR1B = 0b01000001; // set Rising edge
CurrentEdge = Rising;
if (CurrentTime>PreviousTime) T1= CurrentTime-PreviousTime;
else T1= 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
// Place your code here
}
Спасибо за коментарии. Уще вопрос, в коде
accel = accel + ((double)T1/((double)T1+(double)T2)-0.546)/0.125; // 0.546
как вычислить заполнение ШИМ кадра (0.546).
На самом деле число 0.546 - это поправка, должно стоять 0.5 , но в даташите указано, что это коэффициент надо подбирать опытным путём для большей точности. При значении 0.5 данные с датчика немного не соответствовали действительности. А так ускорение считается по такой формуле, которую взял из даташита на датчик ADXL202:
Acceleration(g) = (T1/T2 – 0.5)/12.5%
Советую заглянуть в даташит на ваш датчик, возможно там есть различия.
Radioded, у вас на фотографии акселерометра подключена флэш карта,
не могли бы вы описать схему подключения, алгоритм записи-чтения, если можно на примере.
Да, это карточка MMC, поигрался с ней. Читал блоками по 512 байт, с FAT не стал заморачиваться. Она в моей конструкции не использовалась, эту плату я изначально использовал как макетную. Исходники для работы с картами памяти SD / MMC я выкладывал здесь: https://radioded.ru/forum/viewtopic.php?f=7&t=34 .
Radioded, посоветуй как лучше сделать коммутацию, с акселлерометра два выхода ШИМ X и Y, а в mega16 один вход таймера захвата?
Можно вместо ICP, использовать ноги INT0 и INT1 (в DIP корпусе это 16 и 17 ноги). И по возникновению прерывания считывать значения счетчика TCNT1 и производить вычисления периодов лог. "1" в ШИМ, а дальше простая арифметика.
Важно, не забывать переключать режимы прерываний на этих ножках т.е. ставить то по спадающему фронту то по нарастающему фронту, чтобы захватывать моменты изменения сигнала на этих ножках.
Пришли датчики, спаял схему по даташиту. Включил, ничего не работает, начал разбираться по элементам. С HMC1021, HMC1022 все стало понятно но в операционнике пришлось увеличить коэффицент умножения что бы повысить разрешающую способность.
Дальше очередь дошла до ADXL213, Rset установил 125кОм, на осцилографе сигнал меняет форму при наклонах датчика, а прога не считает правильно Т1 и Т2 вот код, подскажите в чем может быть проблема
#include <mega16.h>
#include <stdio.h>
#include <delay.h>
#include <stdlib.h>
#include <math.h>
#define Rising 1
#define Falling 0
double accel = 0;
unsigned int T1=0, T2=0, PreviousTime, CurrentTime;
unsigned char CurrentEdge;
float roll;
interrupt [TIM1_CAPT] void timer1_capt_isr(void)
{
if (CurrentEdge == Rising){
CurrentTime = ICR1L;
CurrentTime += (int)ICR1H<<8;
TCCR1B = 0b00000010; //ск/8
CurrentEdge = Falling;
if (CurrentTime>PreviousTime) T2 = CurrentTime-PreviousTime;
else T2 = 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
} else { // if Edge Falling
CurrentTime = ICR1L;
CurrentTime += (int)ICR1H<<8;
TCCR1B = 0b01000010; // set Rising edge ск/8
CurrentEdge = Rising;
if (CurrentTime>PreviousTime) T1 = CurrentTime-PreviousTime;
else T1= 65535-PreviousTime+CurrentTime;
PreviousTime=CurrentTime;
}
printf("T1=%d",T1);
putchar(0x0D);
printf("T2=%d",T2);
putchar(0x0D);
accel = ((double)T1/((double)T1+(double)T2)-0.5)/0.3;
roll = asin(accel/9.81)*180/3.14; // пересчет ускорения в угол наклона и перевод из радиан в градусы
printf("accel=%f",accel);
putchar(0x0D);
printf("roll=%f",roll);
putchar(0x0D);
}
void main(void)
{
UCSRA=0x00;
UCSRB=0x18;
UCSRC="0x86;"
UBRRH=0x00;
UBRRL=0x19;
ACSR=0x80;
SFIOR=0x00;
TIMSK |= (1<< TICIE1);
TCCR1A = 0;
TCCR1B=0b11000010;
TCNT1 = 0;
CurrentEdge = Rising;
#asm("sei")
while (1) ;
delay_ms(3000);
}
Частота кварца 4 мгц, Т1 выдает в горизонтальном положении 26589, Т2=26730
в повернутом на 90гр Т1= 25851 Т2=26120 хотя на осцилографе Т1 как минимум в три раза больше Т2
Доброго времени суток!
Просмотрел код представленный mas_nk И появился вопрос вот какого рода:
1. В расчете ускорения в размерности g
accel = ((double)T1/((double)T1+(double)T2)-0.5)/0.3; откуда берется коэффициент 0,3 ?
2. Вызывает интерес ещё место в исходном коде
TCCR1B = 0b01000010;
CurrentEdge = Rising;
получается, что фильтрация входного сигнала ICP будет отключена после первого же срабатывания прерывания и далее никогда не будет применяться. Или я ошибаюсь?