Технический форум по робототехнике.
BeInspired » 30 ноя 2011, 21:49
Всем доброго времени суток.
Вопрос - написал программу, которая считывает текущее состояние двух каналов АЦП (на них нацеплены переменные резисторы), вычисляет значение напряжений. По результату выдает направление вращения для моторов и величину ШИМ.
Вот программа:
- Код: Выделить всё • Развернуть
#include <avr/io.h>
//определим каналы ШИМа
#define SHIM1 OCR1A
#define SHIM2 OCR1B
//определим входные каналы для джойстика:
/*0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7*/
#define CHANNEL1 0x00
#define CHANNEL2 0x01
//Програма задержки
void pause (unsigned int a)
{
unsigned int i;
for (i=a;i>0;i--);
}
//функция получает в качестве аргумента номер канала и
//возвращает значение на входе
unsigned int ADC_result(unsigned char adc_input)
{
ADMUX=adc_input | (ADMUX & 0xFF);
//задержка для стабилизации входного напряжения
pause(100);
//начинаем преобразование (ADSC = 1)
ADCSRA = ADCSRA | 0x40;
while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
ADCSRA|=0x10;//устанавливаем ADIF
return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}
void main(void)
{
unsigned int i;
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
float voltages[3] = {3.5, 1.5, 0};
//Массив значений мощности ШИМ (без учета знака)
int power[3] = {0xFF, 0x00, 0xFF};
int direction[3] = {1, 0, -1};
//значения вольтажа из соответствующих резисторов
float vl1, vl2;
//направление вращения моторов
// 0 - нет вращения
// 1 - нормальное направление вращения
// -1 - обратное вращение
int dir1, dir2;
DDRB=0x07; //Инициализация PB1 (OC1A) и PB2 (OC1B) как выход, а также PB0 - светодиод
DDRD=0xFF; // все ножки порта D сделать выходами
//зажигаем светодиод
PORTB = PORTB | 0x01;
//Инициализируем ШИМ
TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10)|(1<<WGM12); //На выводе OC1A единица, когда OCR1A==TCNT1, восьмибитный ШИМ
TCCR1B=(1<<CS10)|(1<<WGM12);//Делитель=
//Иницилиазируем АЦП
ADMUX = 0xC0;// Выбрали в качестве опорного напряжения внутренний 2.56В с кондером. 1100.0000 = 0xC0
ADCSRA = 0x8D;
while (1)
{
//////////////////////////////////////////////////////////////////////////
// ВОЛЬТАЖ
//////////////////////////////////////////////////////////////////////////
vl1 = 0.0049 * ADC_result(CHANNEL1);
vl2 = 0.0049 * ADC_result(CHANNEL2);
for (i=0; i<2; i++)
{
if (vl1 >= voltages[i])
{
//если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
SHIM1 = power[i];
dir1 = direction[i];
break;//сбрасываем цикл, иначе дойдем до самого первого значения
}
}
for (i=0; i<2; i++)
{
if (vl2 >= voltages[i])
{
//если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
SHIM2 = power[i];
dir2 = direction[i];
break;//сбрасываем цикл, иначе дойдем до самого первого значения
}
}
//устанавливаем направление вращения
//для ножек PD0 и PD1 (первый мотор) PD2 и PD3 (второй мотор)
// ПЕРВЫЙ МОТОР
// 10 00 - прямое вращение
// 01 00 - обратное
// 00 00 - СТОП
// ВТОРОЙ МОТОР
// 00 10 - прямой
// 00 01 - обратный
// 00 00 - СТОП
PORTD = 0x00;//СТОП обоих моторов по умолчанию dir1=dir2=0
if (dir1 == 1)
{
PORTD = PORTD | 0x08;
}
if (dir1 == -1)
{
PORTD = PORTD | 0x04;
}
if (dir2 == 1)
{
PORTD = PORTD | 0x02;
}
if (dir2 == -1)
{
PORTD = PORTD | 0x01;
}
}
}
Вопроса два:
1. Почему программа реагирует только на изменения ADC1 (ножка PC1)? На первый АЦП, который ADC0 не реагирует ни один мотор. А вот на вторую почему-то реагируют оба мотора.
2. Почему размер такой маленькой программы огромный? Почти 9 килобайт hex-файл. Как уменьшить?
Во вложениях прошивка и плата.
- Вложения
-
- plata-test1.rar
- (18.35 КиБ) Скачиваний: 0
-
- AVRGCC1.hex
- (8.73 КиБ) Скачиваний: 271
Последний раз редактировалось
BeInspired 01 дек 2011, 22:02, всего редактировалось 1 раз.
Dmitry__ » 30 ноя 2011, 22:05
BeInspired писал(а):Почему размер такой маленькой программы огромный?
зачем для решения такой простой задачи использовать математику с плавающей точкой ?
Добавлено спустя 3 минуты 16 секунд:вдумайся, ацп авра 10 бит = 1024 значений
ой, или 12 бит?
boez » 01 дек 2011, 14:28
BeInspired писал(а):1. Почему программа реагирует только на изменения ADC1 (ножка PC1)? На первый АЦП, который ADC0 не реагирует ни один мотор. А вот на вторую почему-то реагируют оба мотора.
Вот поэтому:
- Код: Выделить всё • Развернуть
ADMUX=adc_input | (ADMUX & 0xFF);
Подумай, что получится в admux, когда у тебя был включен первый канал, а ты хочешь включить нулевой?
BeInspired » 01 дек 2011, 16:37
>>Подумай, что получится в admux, когда у тебя был включен первый канал, а ты хочешь включить нулевой?
Точно - спасибо большое! Сегодня поправлю и попробую.
Dmitry__ писал(а):зачем для решения такой простой задачи использовать математику с плавающей точкой ?
Т.е. думаете только из-за этого? Ну не помню я чтобы в С++ при изменении типов данных так кардинально менялся размер программы...
Dmitry__ » 01 дек 2011, 17:50
про авр не скажу, а в msp430 библиотека плавающей точки занимает около 2-х кб. (это около 4 кб. в hex )
что пишет xx.map ? сколько там твоего кода, а сколько там всяких _Mul32f, _Div32f, _Add32f и прочей нечисти.
шо мешает сделать так:
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
unsigned int voltages[3] = {716, 307, 0};
{3.5, 1.5, 0} - преобразовать:
1024 (разрядность ацп) / 5(вольт питания ацп) * 3.5 (ацкое число) = 716
Добавлено спустя 3 минуты 24 секунды:
есно это выкинуть:
vl1 = 0.0049 * ADC_result(CHANNEL1);
vl2 = 0.0049 * ADC_result(CHANNEL2);
BeInspired » 01 дек 2011, 19:17
Dmitry__ писал(а):про авр не скажу, а в msp430 библиотека плавающей точки занимает около 2-х кб. (это около 4 кб. в hex )
что пишет xx.map ? сколько там твоего кода, а сколько там всяких _Mul32f, _Div32f, _Add32f и прочей нечисти.
шо мешает сделать так:
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
unsigned int voltages[3] = {716, 307, 0};
{3.5, 1.5, 0} - преобразовать:
1024 (разрядность ацп) / 5(вольт питания ацп) * 3.5 (ацкое число) = 716
Добавлено спустя 3 минуты 24 секунды:
есно это выкинуть:
vl1 = 0.0049 * ADC_result(CHANNEL1);
vl2 = 0.0049 * ADC_result(CHANNEL2);
Ничего не мешает - сейчас поправлю и попробую
Апдейт. Попробовал - я в шоке... код уменьшился на 6(!) килобайт. Спасибо!
boez » 01 дек 2011, 19:44
Так ничего удивительного тут нет. На С++ под интел во-первых сущственно больше сами объемы библиотек, во-вторых совсем не факт, что неиспользование плавающей точки в программе влечет ее реальное неиспользование в генерируемом исполняемом файле. Ну и операции с плавающей точкой для интела - это просто вызовы сопроцессора. А здесь все именно так, плавающая точка - эмуляция, под каждую операцию подключается библиотечная функция - для преобразований в целые и обратно, для умножений-сложений и т.п., и все это довольно объемное и жутко медленное к тому же, поскольку даже одинарная точность float - это 32 бита, а процессор 8-битный.
BeInspired » 01 дек 2011, 21:01
- AVRGCC1.hex
- Обновленная прошивка
- (2.51 КиБ) Скачиваний: 241
Не могу нормально отладить - протеус почему-то наглухо виснет через 30 секунд после начала симуляции
У вас не виснет?
Переделал программу - посмотрите, пожалуйста - нет ли явных косяков?
- Код: Выделить всё • Развернуть
#include <avr/io.h>
//определим каналы ШИМа
#define SHIM1 OCR1A
#define SHIM2 OCR1B
//определим входные каналы для джойстика:
/*0000 ADC0
0001 ADC1
0010 ADC2
0011 ADC3
0100 ADC4
0101 ADC5
0110 ADC6
0111 ADC7*/
#define CHANNEL1 0x00
#define CHANNEL2 0x01
//Програма задержки
void pause (unsigned int a)
{
unsigned int i;
for (i=a;i>0;i--);
}
//функция получает в качестве аргумента номер канала и
//возвращает значение на входе
unsigned int ADC_result(unsigned char adc_input)
{
ADMUX = 0xC0;
ADMUX=adc_input | ADMUX ;
//задержка для стабилизации входного напряжения
pause(100);
//начинаем преобразование (ADSC = 1)
ADCSRA = ADCSRA | 0x40;
while((ADCSRA & 0x10)==0); //ждем, пока АЦП закончит преобразование (ADIF = 0)
ADCSRA|=0x10;//устанавливаем ADIF
return ADCW;//ADCW - содержит ADCH и ADCL как нам нужно
}
void main(void)
{
unsigned int i;
//Массив, хранящий в себе пороговые значения вольтажей от джойстика
int voltages[7] = {896, 768, 640, 384, 256, 128, 0};
//Массив значений мощности ШИМ (без учета знака)
int power[7] = {0xFF, 0xCC, 0x80, 0x00, 0x80, 0xCC, 0xFF};
int direction[7] = {1, 1, 1, 0, -1, -1, -1};
//значения вольтажа из соответствующих резисторов
int vl1, vl2;
//направление вращения моторов
// 0 - нет вращения
// 1 - нормальное направление вращения
// -1 - обратное вращение
int dir1, dir2;
DDRB=0x07; //Инициализация PB1 (OC1A) и PB2 (OC1B) как выход, а также PB0 - светодиод
DDRD=0xFF; // все ножки порта D сделать выходами
//зажигаем светодиод
PORTB = PORTB | 0x01;
//Инициализируем ШИМ
TCCR1A=(1<<COM1A1)|(1<<COM1B1)|(1<<WGM10)|(1<<WGM12); //На выводе OC1A единица, когда OCR1A==TCNT1, восьмибитный ШИМ
TCCR1B=(1<<CS10)|(1<<WGM12);//Делитель= /1
//Иницилиазируем АЦП
//воспользуемся значениями регистров, определенными ранее
ADMUX = 0xC0;// Выбрали в качестве опорного напряжения внутренний 2.56В с кондером. 1100.0000 = 0xC0
ADCSRA = 0x8F;
while (1)
{
//////////////////////////////////////////////////////////////////////////
// ВОЛЬТАЖ
//////////////////////////////////////////////////////////////////////////
vl1 = ADC_result(CHANNEL1);
vl2 = ADC_result(CHANNEL2);
for (i=0; i<8; i++)
{
if (vl1 >= voltages[i])
{
//если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
SHIM1 = power[i];
dir1 = direction[i];
break;//сбрасываем цикл, иначе дойдем до самого первого значения
}
}
for (i=0; i<8; i++)
{
if (vl2 >= voltages[i])
{
//если вольтаж больше определенного значения из массива, то выбираем соответствующее ему значение мощности
SHIM2 = power[i];
dir2 = direction[i];
break;//сбрасываем цикл, иначе дойдем до самого первого значения
}
}
//устанавливаем направление вращения
//для ножек PD0 и PD1 (первый мотор) PD2 и PD3 (второй мотор)
// ПЕРВЫЙ МОТОР
// 10 00 - прямое вращение
// 01 00 - обратное
// 00 00 - СТОП
// ВТОРОЙ МОТОР
// 00 10 - прямой
// 00 01 - обратный
// 00 00 - СТОП
PORTD = 0x00;//СТОП обоих моторов по умолчанию dir1=dir2=0
if (dir1 == 1)
{
PORTD = PORTD | 0x08;
}
if (dir1 == -1)
{
PORTD = PORTD | 0x04;
}
if (dir2 == 1)
{
PORTD = PORTD | 0x02;
}
if (dir2 == -1)
{
PORTD = PORTD | 0x01;
}
}
}
Последний раз редактировалось
BeInspired 01 дек 2011, 22:01, всего редактировалось 1 раз.
Dmitry__ » 01 дек 2011, 21:29
ты бы код на форум между [code][/code] вставлял, приятнее читать
BeInspired » 01 дек 2011, 22:02
Добавил тег. Просто сначала его поискал и не заметил.
Dmitry__ » 01 дек 2011, 23:58
ого, дык это же программа (с тегом)
по мне, так все замечательно, лучшее - враг хорошего.
Ну, может если только:
unsigned int ADC_result(unsigned char adc_input)
int vl1, vl2;
vl1 = ADC_result(CHANNEL1);
Но это я придираюсь
Неси зачетку...
BeInspired » 05 дек 2011, 20:37
Как снизить нагрузку на процессор? Мне джойстик надо опрашивать не чаще 20 раз в секунду, а все остальное время желательно чтобы он спал.. Дайте ссылку где почитать про это или подскажите куда копать (очень желательно с примерами на С).
При этом надо, чтобы не отрубался ШИМ.
boez » 06 дек 2011, 14:16
Почитать - в даташите Power Modes. Так, чтоб спал и шимил - не уверен, что получится. А зачем собсно, электричества жалко?
Radist » 06 дек 2011, 20:42
Интересно, зачем нужно усыплять МК? Если надо шимить - значит движки должны крутиться и жрать как минимум десятки мА. Бодрствующий МК жрет единицы мА, спящий - десятки мкА. Экономия единиц мА на фоне десятков - зачем? Обычно в сон МК вгоняют, когда изредка нужно что-то сделать, а питание батарейное и выключатель не вписывается в концепцию. Но вроде это не ваш случай.
BeInspired » 07 дек 2011, 10:14
Моторы питаются отдельной цепью на 24 Вольта. А плата управления с МК - от 5 вольт (батарейки).