AVR Раздел по микроконтроллерам компании Atmel - AVR / ATtiny / ATmega / ATMega128 / ATxmega, вопросы по программированию в AVR studio и все, относящееся к AVR... |
08.01.2024, 16:03
|
#11
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Сообщение от Godzilla82
|
Я же вам совсем не так говорил. Куча лишних действий и к тому же неверные.
|
поясните, чем они лишние и неверные? если выбор канала работает.
|
|
|
|
08.01.2024, 16:31
|
#12
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Если у вас внешний источник опорного напряжения, то достаточно писать
PHP код:
|
ADMUX = channel;
|
Для запуска (в том числе и одиночного) преобразования надо установить бит ADSC в регистре ADCSRA:
PHP код:
|
ADCSRA |= (1‹‹ADSC);
|
В даташите сказано, что бит ADSC будет оставаться установленным, пока идёт одиночное преобразование.
Правильнее пользоваться битом ADIF. Он устанавливается в 1 когда преобразование закончено и в регистр данных записаны новые данные.
Соответственно, надо ждать пока этот бит равен нулю:
PHP код:
|
while((ADCSRA & (1‹‹ADIF)) == 0);
|
После того, как он установился, вы считываете данные и не забываете обнулить его:
PHP код:
|
ADCSRA |= (1‹‹ADIF);
|
Да-да, обнуляется этот бит записью в него единицы.
P.S. Вроде как для одиночных преобразований бит ADSC сбрасывается при окончании преобразований, но для бита ADIF в даташите явно указано
Цитата:
|
This bit is set when an ADC conversion completes and the data registers are updated
|
поэтому лучше не экспериментировать, а использовать ADIF.
|
|
|
Сказали "Спасибо" Godzilla82
|
|
|
08.01.2024, 16:55
|
#13
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Сообщение от Godzilla82
|
поэтому лучше не экспериментировать, а использовать ADIF.
|
ага так яснее. да я про это тожде прочел. по этому у меня есть другой код где я все сделал по правилам, просто его уже выкидывать не стал
Нажмите, чтобы открыть спойлер
PHP код:
|
int readAdc(uint8_t channel)
{
int LowBitValue;
ADCSRA |= (1‹‹ADSC); // Запуск преобразования
while((ADCSRA &(1‹‹ADIF))==0); // Ожидание прерывания
ADCSRA |= (1‹‹ADIF);
LowBitValue = ADCL; // Чтение младшего бита
return((ADCH‹‹8) | LowBitValue); // Возрат прочитанных значений со старшего и младшего бита
}
|
|
|
|
|
09.01.2024, 16:31
|
#14
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Вот я вернулся со своими потугами. Подскажите что я сделал не так.
Написал такой вот код, но показания не держатся, как будто вычисления не останавливаюся
Нажмите, чтобы открыть спойлер
PHP код:
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Переменные за прделелами тела для их глобальности
const double vref = 5; // Опорное напряжение от внешнего источника
const int ADCMAX = 1023; // Максимальное значение АЦП
double volt_devive = 3.999; // коэффициент делителя напряжения
double adcTOvolts=0; // Переменная для преобразования ацп в вольты
int adc0_avgvalue;
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
// Массивы для хранения данных
#define ADC_CHANNEL_NUM 8
uint16_t adc_values[ADC_CHANNEL_NUM]; // Массив для хранения значений АЦП
uint16_t adc_max[ADC_CHANNEL_NUM]; // Массив для хранения максимальных значений
uint16_t adc_min[ADC_CHANNEL_NUM]; // Массив для хранения минимальных значений
uint32_t adc_sum[ADC_CHANNEL_NUM]; // Массив для хранения суммы значений
float adc_avg[ADC_CHANNEL_NUM]; // Массив для хранения средних значений
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void displey_start(void) // Активирую Порт B на выход, для того что бы активировать дисплей
{
DDRB &= ~((1‹‹PINB1) | (1‹‹PINB2) | (1‹‹PINB3) | (1‹‹PINB4) | (1‹‹PINB5));
PORTB = 0x00;
Lcd_init();
Lcd_clear();
Lcd_update();
LcdContrast(62);
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void adc_start(void) // запуск ацп от внешнего ИОН 5v
{
ADCSRA |=(1‹‹ADPS2) | (1‹‹ADPS1) | (1‹‹ADPS0); // Устанавливаю частоту дискретезации 125кгц с помощью делителя на 128
ADMUX |= (1‹‹REFS0); // Внешний ИОН
ADCSRA |=(1‹‹ADEN); // Запускаю работу АЦП
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
int readAdc()
{
int LowBitValue;
ADCSRA |= (1‹‹ADSC); // Запуск преобразования
while(!(ADCSRA &(1‹‹ADIF))); // Ожидание прерывания
ADCSRA |= (1‹‹ADIF);
LowBitValue = ADCL; // Чтение младшего бита
return ((ADCH‹‹8) | LowBitValue); // Возрат прочитанных значений со старшего и младшего бита
}
int main(void)
{
adc_start();
displey_start();
sei();
adcTOvolts = (vref / ADCMAX) * volt_devive;
while (1)
{
for (uint8_t channel = 0; channel ‹ ADC_CHANNEL_NUM; channel++)
{
ADMUX&=0xF0;// очистить биты MUX
ADMUX|=channel;
_delay_us(200);
adc_values[channel] = readAdc(); // Сохраняем результат преобразования
// обновляем максимальное и минимальное значения
if (adc_values[channel] › adc_max[channel]) {adc_max[channel] = adc_values[channel];}
if (adc_values[channel] ‹ adc_min[channel]) {adc_min[channel] = adc_values[channel];}
adc_sum[channel] += adc_values[channel]; // Обновляем сумму значений
adc_avg[channel] = (float)adc_sum[channel] / (float)(ADC_CHANNEL_NUM + 1); // Вычисляем среднее значение
}
adc0_avgvalue = adc_avg[0];
Lcd_clear();
Lcd_printf(0,0, FONT_1X, adc0_avgvalue, 0); //Вывод с нулевого канала
Lcd_update();
_delay_us(400);
}
}
|
Либо есть еще один вариант. Но проблема в том что при выводе двух каналов он показывает минимальное и максимальное значение от первого канала и они отображаются на всех. И при достижении нуля или максимума, показания не обновляются больше
Нажмите, чтобы открыть спойлер
PHP код:
|
value = readAdc(channel); // Прочитать значение с канала
_delay_us(200);
if (value ‹ min) min = value; // Обновить минимальное значени
if (value › max) max = value; // Обновить максимальное значение
sum += value; // Добавить значение к сумме
sampleCount++;
if (sampleCount ›= 40)
{
average = sum / 40; // Вычислить среднее значение
}
int adc0_avgvalue = readAdc(0);
int adc0_min = min;
int adc0_max = max;
int adc1_avgvalue = readAdc(1);
int adc1_min = min;
int adc1_max = max;
Lcd_clear();
Lcd_printf(0,0, FONT_1X, adc0_avgvalue*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_printf(0,1, FONT_1X, adc0_max*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_printf(8,1, FONT_1X, adc0_min*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_printf(0,3, FONT_1X, adc1_avgvalue*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_printf(0,4, FONT_1X, adc1_max*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_printf(8,4, FONT_1X, adc1_min*adcTOvolts, 3); //Вывод с нулевого канала
Lcd_update();
_delay_us(400);
|
Последний раз редактировалось Devil Byte; 09.01.2024 в 17:54.
|
|
|
|
09.01.2024, 18:50
|
#15
|
Заблокирован
Регистрация: 07.09.2014
Адрес: В Кремле!
Сообщений: 4,486
Сказал спасибо: 396
Сказали Спасибо 2,220 раз(а) в 1,319 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Ну потому что надо все-таки разобраться, что такое МАССИВЫ.
Одномерный массив - это последовательно расположенные ячейки памяти. array[index], где index - номер ячейки (начиная с 0)
Многомерный массив - это последовательно расположенные в памяти одномерные массивы array[line][index], в которых line - номер одномерного массива (начиная с 0), а index - номер ячейки одномерного массива.
Следовательно, как я показывал выше, для накопления измерений в нескольких каналах вам нужен многомерный массив на N измерений: adc_meash[CHANNEL][N]. Для мин., макс. и средн. в том же стиле нужны одномерные массивы min[CHANNEL], max[CHANNEL], average[CHANNEL],
в которых индекс CHANNEL обозначает номер канала.
|
|
|
|
09.01.2024, 19:22
|
#16
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Сообщение от NewWriter
|
Ну потому что надо все-таки разобраться, что такое МАССИВЫ.
.
|
Я все еще пытаюсь, к сожалению мало времени на попытки занятий. В варианте ниже я вообще попробовал отказаться от массивов. Получил почти то что хотелно с несколько странным результатом
|
|
|
|
09.01.2024, 20:24
|
#17
|
Гуру портала
Регистрация: 06.05.2005
Адрес: Краснодар, возле укротворного моря.
Сообщений: 19,084
Сказал спасибо: 2,564
Сказали Спасибо 11,898 раз(а) в 5,971 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Сообщение от Devil Byte
|
Я все еще пытаюсь, к сожалению мало времени на попытки занятий
|
Что тут сложного?
Сообщение от NewWriter
|
Одномерный массив - это последовательно расположенные ячейки памяти. array[index], где index - номер ячейки (начиная с 0)
|
Ну, представьте себе цепь, у каждого звена которой есть свой номер (индекс). Содержимое "звена" достается обращением к звену с нужным номером.
Сообщение от NewWriter
|
Многомерный массив - это последовательно расположенные в памяти одномерные массивы array[line][index], в которых line - номер одномерного массива (начиная с 0), а index - номер ячейки одномерного массива.
|
Несколько цепей одинаковой длины. Обращение к звену выбором номера цепи и номера звена. Это двумерный массив. Еще его можно представить обычной таблицей, доступ к ячейкам по номеру сроки и столбца.
__________________
Не бейте больно, ежели чо, ну не удержался... А вааще,
"Мы за все хорошее, против всей х..., По лугам некошеным чтобы шли ступни,
Чтобы миром правила правда, а не ложь, Мы за все хорошее, нас не на...!
..." (Ленинград)
Я не несу ответственности за свои действия в Вашей голове.
|
|
|
|
09.01.2024, 20:56
|
#18
|
Почётный гражданин KAZUS.RU
Регистрация: 29.10.2006
Сообщений: 1,446
Сказал спасибо: 99
Сказали Спасибо 317 раз(а) в 233 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Начнём-с...
Сообщение от Devil Byte
|
PHP код:
|
void adc_start(void) // запуск ацп от внешнего ИОН 5v { ADCSRA |=(1‹‹ADPS2) | (1‹‹ADPS1) | (1‹‹ADPS0); ADMUX |= (1‹‹REFS0); // Внешний ИОН ADCSRA |=(1‹‹ADEN); // Запускаю работу АЦП }
|
|
Внешний ИОН - это когда REFS0 и REFS1 равны нулю.
У вас REFS0 равен единице. Соответственно, это не внешний ИОН, а AVCC. У вас вывод AREF куда подключён?
Вы всё время устанавливаете биты (везде "|="). Правильно будет, если сразу присвоить нужное значение.
Сообщение от Devil Byte
|
PHP код:
|
int LowBitValue;
|
|
Всё-таки не бит, а байт. Это совершенно разные вещи. У вас байт. И ни к чему байт объявлять как int.
Сообщение от Devil Byte
|
PHP код:
|
return ((ADCH‹‹8) | LowBitValue);
|
|
Вместо этого можно писать return ADC или return ADCW.
В файле iom128.h есть определение:
Цитата:
|
PHP код:
|
#define ADCW _SFR_IO16(0x04) #define ADC _SFR_IO16(0x04) #define ADCL _SFR_IO8(0x04) #define ADCH _SFR_IO8(0x05)
|
|
Компилятор сам сначала прочитает младший байт, потом старший.
Тут некоторые советовали подстраховаться и писать явно. Будете так делать, когда станете менять компиляторы как перчатки.
Сообщение от Devil Byte
|
|
Вы же не используете прерывания. Зачем вы их разрешаете?
Сообщение от Devil Byte
|
PHP код:
|
ADMUX&=0xF0;// очистить биты MUX
|
|
Если уж хотите очистить биты MUX, нужно писать ADMUX &= 0xE0. Потому, что битов MUX - пять, а вы очищаете только четыре.
Проще, быстрее выполняется и меньше занимает места в памяти:
PHP код:
|
ADMUX = channel | (1‹‹REFS0);
|
Сообщение от Devil Byte
|
PHP код:
|
adc_avg[channel] = (float)adc_sum[channel] / (float)(ADC_CHANNEL_NUM + 1); // Вычисляем среднее значение
|
|
Вы почему-то делите сумму на количество каналов + 1 (я бы ещё понял, если просто на количество каналов). А сумма постоянно растёт, ведь вы снова и снова делаете измерения и добавляете результат в сумму.
Надо в какой-то переменной хранить количество просуммированных значений, чтобы потом делить сумму не на странную константу, а на реальное количество измеренных значений, содержащихся в сумме.
Рано или поздно возникнет переполнение переменной, хранящую сумму значений. Это неверный подход.
|
|
|
|
09.01.2024, 21:09
|
#19
|
Заблокирован
Регистрация: 07.09.2014
Адрес: В Кремле!
Сообщений: 4,486
Сказал спасибо: 396
Сказали Спасибо 2,220 раз(а) в 1,319 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
самое смешное, что я ж еще в #4 посте на прошлой странице показал, как вычислить среднее значение и найти мин-макс, как работать с массивами.
Да, я понимаю, когда нет склонности к программированию, тогда даже простые действия даются с большим трудом... А ведь программирование - это не просто "написать кучу букавок и посмотреть как на экранчике цифры бегают". Программирование - это сложная умственая задача, требующая хорошей памяти и ясного мышления.
|
|
|
|
10.01.2024, 11:53
|
#20
|
Временная регистрация
Регистрация: 07.03.2019
Сообщений: 87
Сказал спасибо: 11
Сказали Спасибо 0 раз(а) в 0 сообщении(ях)
|
Re: Запись переменной в массив и дальнейшая работа с содержимым
Сообщение от Godzilla82
|
Внешний ИОН - это когда REFS0 и REFS1 равны нулю.
У вас REFS0 равен единице. Соответственно, это не внешний ИОН, а AVCC. У вас вывод AREF куда подключён?
Вы всё время устанавливаете биты (везде "|="). Правильно будет, если сразу присвоить нужное значение.
|
Ясно. Но для чего тогда служит опция при включении 1‹‹REFS0??
Сообщение от Godzilla82
|
Всё-таки не бит, а байт. Это совершенно разные вещи. У вас байт. И ни к чему байт объявлять как int.
|
Тут я стараюсь заставлять себя объявляить тип перемеменных как uint8_t и так далее в зависимости от потребности. но пока еще не везде это делаю. Думаю что скоро исправллюсь.
Сообщение от Godzilla82
|
Вы всё время устанавливаете биты (везде "|=")
|
Принял к сведению, буду стараться сразу присваивать нужное значение
Сообщение от Godzilla82
|
Вместо этого можно писать return ADC или return ADCW.
|
По возвррату сразу все стало на свои места. а то я только каких теорий не наслушался.. и что эта запись обязательная и что несли написать return ADC то он может и не прочесть)
Скажите а что по поводу записи ожадания завершения преобразования -- правильно сденлать вот так
Код:
|
while(!(ADCSRA &(1‹‹ADIF))); // Ожидание прерывания
ADCSRA |= (1‹‹ADIF); |
или можно использовать что то типа этого
Код:
|
while(!(ADCSRA &(1‹‹ADSC))) |
??
Сообщение от Godzilla82
|
ВВы же не используете прерывания. Зачем вы их разрешаете?.Если уж хотите очистить биты MUX
|
Глобальные нет. А разве Установка флага ADIF не относится к прерыванию? или оно распространяется только на ацп и по этому не нужно глобально разрешать?
Сообщение от Godzilla82
|
Если уж хотите очистить биты MUX
|
Это был еще один совет...мол некоторые говорят что лучше сбрасывать биты.
Сообщение от Godzilla82
|
Это неверный подход.
|
Это тоже понял.
Вчера под утро немного исправил код, пока отказался от массивов. проверка АЦП = 614 так как я на входе делитель напряжения использую
Вроде работает
Нажмите, чтобы открыть спойлер
PHP код:
|
//---------------------------------------------------------------------------------------------------------------------------------------------------------------------- // Переменные за прделелами тела для их глобальности
const double vref = 5; // Опорное напряжение от внешнего источника const int ADCMAX = 1023; // Максимальное значение АЦП double volt_devive = 3.999; // коэффициент делителя напряжения //double adcTOvolts=0; // Переменная для преобразования ацп в вольты
// Глобальные переменные для отслеживания минимальных и максимальных значений int adc_min_value = 614; // начнем с максимально возможного значения АЦП. Оно 614 так как используется делитель напряжения на входе int adc_max_value = 0; // начнем с минимально возможного int adc_value; #define SAMPLE_COUNT 52 uint16_t adcSamples[SAMPLE_COUNT];
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void displey_start(void) // Активирую Порт B на выход, для того что бы активировать дисплей { DDRB &= ~((1‹‹PINB1) | (1‹‹PINB2) | (1‹‹PINB3) | (1‹‹PINB4) | (1‹‹PINB5)); PORTB = 0x00; Lcd_init(); Lcd_clear(); Lcd_update(); LcdContrast(62); } //---------------------------------------------------------------------------------------------------------------------------------------------------------------------- void adc_start(void) // запуск ацп от внешнего ИОН 5v { ADCSRA |=(1‹‹ADPS2) | (1‹‹ADPS1) | (1‹‹ADPS0); // Устанавливаю частоту дискретезации 125кгц с помощью делителя на 128 ADMUX |= (1‹‹REFS0); // Внешний ИОН ADCSRA |=(1‹‹ADEN); // Запускаю работу АЦП } //---------------------------------------------------------------------------------------------------------------------------------------------------------------------- uint16_t readAdc(uint8_t channel) {
ADMUX|=channel; _delay_us(100); ADCSRA |= (1‹‹ADSC); // Запуск преобразования while(!(ADCSRA &(1‹‹ADIF))); // Ожидание прерывания ADCSRA |= (1‹‹ADIF); return ADCW; // Возрат прочитанных значений со старшего и младшего бита }
int main(void) { adc_start(); displey_start(); double adcTOvolts = (vref / ADCMAX) * volt_devive;
while (1) {
adc_value = readAdc(0); // Прочитать значение с канала // Проверка на сброс переменной adc_min_value в случае достижения значения 0 if (adc_value == 0) { adc_min_value = 0; } // Проверка на новое минимальное значение, если adc_value не равно 0 или меньше текущего значения adc_min_value else if (adc_value ‹ adc_min_value || adc_min_value == 0) { adc_min_value = adc_value; }
// Проверка на установку переменной adc_max_value в случае достижения значения 1023 или 0 if (adc_value == 614 || adc_value == 0) { adc_max_value = adc_value; } // Проверка на новое максимальное значение, если adc_value не равно 1023 или 0, или больше текущего значения adc_max_value else if ((adc_value › adc_max_value && adc_value != 614) || adc_max_value == 614 || adc_max_value == 0) { adc_max_value = adc_value; } Lcd_clear(); Lcd_printf(0,0, FONT_1X, adc_value, 0); //Вывод с нулевого канала Lcd_printf(0,1, FONT_1X, adc_min_value,0 ); //Вывод с нулевого канала Lcd_printf(8,1, FONT_1X, adc_max_value, 0); //Вывод с нулевого канала Lcd_update(); _delay_ms(200); } }
|
Последний раз редактировалось Devil Byte; 10.01.2024 в 16:09.
|
|
|
|
Ваши права в разделе
|
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения
HTML код Выкл.
|
|
|
Часовой пояс GMT +4, время: 22:28.
|
|