Zhuk72, то есть получается так: ISR (TIMER0_OVF_vect) заменяю на ISR (TIMER2_OVF_vect)
Обработчик прерывания по переполнению таймера вставляю перед главной программой int main (void) ISR (TIMER2_OVF_vect) { LED_DIG_PORT |= (1<<LED_DIG_1A)|(1<<LED_DIG_1B)|(1<<LED_DIG_2A)|(1<<LED_DIG_2B)|(1<<LED_DIG_3A)|(1<<LED_DIG_3B); // Гасим все разряды }
Настройки таймера такие TCCR2 |= (1 << WGM21)|(1 << WGM20)|(1 << CS21); // Режим FASTPWM. Предделитель на 8 TIMSK |= (1 << TOIE2)|(1 << OCIE2); // Разрешение прерываний по таймеру 2 OCR2 = 50; // Начальное значение OCR2
Zhuk72, компилятор сыпет ошибками что не знает такого таймера. Полез в даташит, оказывается действительно у ATTiny261A нет такого таймера. Это получается что яркость семисегментного индикатора уменьшить программно не получиться на этом микроконтроллере?
Так с этого и надо было начинать, с чтения ДШ. Да и тип МК вы держали в секрете. Если есть 2 таймера, то можно легко сделать. Да и с одним тоже с чуть большими движениями.
_________________ Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Использование модульных источников питания открытого типа широко распространено в современных устройствах. Присущие им компактность, гибкость в интеграции и высокая эффективность делают их отличным решением для систем промышленной автоматизации, телекоммуникационного оборудования, медицинской техники, устройств «умного дома» и прочих приложений. Рассмотрим подробнее характеристики и особенности трех самых популярных вариантов AC/DC-преобразователей MW открытого типа, подходящих для применения в промышленных устройствах - серий EPS, EPP и RPS представленных на Meanwell.market.
Я атмелами не занимаюсь, поэтому надо дш смотреть. С двумя таймерами принцип такой: в конце прерывания первого таймера заряжаете и запускаете второй таймер, а уже в прерывании второго отключаете индикатор и останавливаете таймер. Период второго таймера должен быть меньше, чем у первого. Чем он будет меньше, тем ниже яркость свечения индикатора.
_________________ Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
вы не учитываете, что у этой тиньки развитая система цифровых компараторов для таймеров. чтобы регулировать яркость при динамической индикации достаточно одного таймера, общий принцип я описывал здесь: http://arv.radioliga.com/content/view/101/49/
вы не учитываете, что у этой тиньки развитая система цифровых компараторов для таймеров.
Потому и сказал, что не занимаюсь Атмелами Я описал общий случай, не привязанный к конкретному железу. Если можно обойтись одним таймером с компаратором - тем лучше.
_________________ Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
Похоже что нужно юзать Timer1 у него есть Fast PWM Mode.
Цитата:
The fast Pulse Width Modulation or fast PWM mode (PWM1A/PWM1B = 1 and WGM1[1:0] = 00) provides a high frequency PWM waveform generation option. The fast PWM differs from the other PWM option by its single-slope operation. The counter counts from BOTTOM to TOP (defined as OCR1C) then restarts from BOTTOM. In non-inverting Compare Output mode the Waveform Output (OCW1x) is cleared on the Compare Match between TCNT1 and OCR1x and set at BOTTOM. In inverting Compare Output mode, the Waveform Output is set on Compare Match and cleared at BOTTOM. In complementary Compare Output mode the Waveform Output is cleared on the Compare Match and set at BOTTOM.
Электpониk, принцип прост как бублик и состоит в следующем берете 8 разрядный таймер и настраиваете его на режим Fast PWM Mode в прерывании по переполнению зажигаете нужный анод, нужного разряда и выводите цифру, а в сравнении весь этот зоопарк тушим
Принцип я понимаю. Вот только проблема в том, что у ATTINY261A нет Timer2. Но есть Timer1 с режимом FastPWM. Можно его задействовать. Правда, сложность в том, что у него несколько компараторов и куча разнообразных режимов и регистров. Ну не беда, после изучения даташита, стали понятны назначения регистров Timer1, и как их настраивать. Переделал программу на Timer1, но проблема в том, что не срабатывает прерывание по сравнению вообще. Подскажите в чём моя ошибка. Может что-то упустил.
ISR (TIMER1_OVF_vect) // Прерывание по переполнению таймера 1 { Отображение цифр на сегментах }
ISR (TIMER1_COMPA_vect) //Прерывание по сравнению таймера 1 { Гасим все разряды }
// Главная программа int main (void) { // Настраиваю таймер 1 для ШИМ TCCR1A |= (0 << COM1A0)|(0 << COM1A1)|(1 << PWM1A); // Ножки микроконтроллера отключены от компаратора. Режим FASTPWM. TCCR1B |= (1 << CS13)|(0 << CS12)|(1 << CS11)|(1 << CS10); //Предделитель на 1024, частота индикатора 2,6 кГц. TCCR1D |= (0 << WGM10)|(0 << WGM11); //Режим FASTPWM TIMSK |= (1 << TOIE1)|(1 << OCIE1A); // Разрешение прерываний таймера 1 по переполнению и сравнению OCR1A = 50; // Начальное значение OCR1A. Яркость индикатора.
...остальная часть программы. Считывание температуры и вывод на индикатор. }
Полный код программы под спойлером. Спойлер#define F_CPU 8000000UL // Тактовая частота микроконтроллера 8 МГц #define LEDS_OK // Раскомментировать, если используется индикатор с общим катодом
// Массив для перекодировки цифры в набор сегментов для семисегментного индикатора const uint8_t codes[16]= { (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_F), // цифра 0 (1<<LED_SEG_B)|(1<<LED_SEG_C), // цифра 1 (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_G), // цифра 2 (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_G), // цифра 3 (1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // цифра 4 (1<<LED_SEG_A)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // цифра 5 (1<<LED_SEG_A)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // цифра 6 (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_F), // цифра 7 (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // цифра 8 (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // цифра 9 0b00000000, // пробел (код 10) (1<<LED_SEG_G), // прочерк (код 11) (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_F)|(1<<LED_SEG_G), // символ градуса (код 12) (1<<LED_SEG_A)|(1<<LED_SEG_B)|(1<<LED_SEG_C)|(1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_F)|(1<<LED_SEG_G)|(1<<LED_SEG_DP), // зажечь все сегменты (код 13) (1<<LED_SEG_D)|(1<<LED_SEG_E)|(1<<LED_SEG_G), // буква "c" для вывода слова "crc" (код 14) (1<<LED_SEG_E)|(1<<LED_SEG_G) // буква "r" для вывода слова "crc" (код 15) };
#define SEG_BLANK 10 // пробел #define SEG_MINUS 11 // прочерк #define SEG_DEGREE 12 // символ градуса #define SEG_ALL 13 // зажечь все сегменты #define SEG_C 14 // буква "c" для вывода слова "crc" #define SEG_R 15 // буква "r" для вывода слова "crc"
volatile uint8_t digit = 0; // Номер текущего знакоместа динамической индикации volatile uint8_t digit1 = 0; // Цифра или символ, выводимые в 1 знакоместо volatile uint8_t digit2 = 0; // Цифра или символ, выводимые во 2 знакоместо volatile uint8_t digit3 = 0; // Цифра или символ, выводимые в 3 знакоместо
uint8_t DS_scratchpad[9] = {0,0,0,0,0,0,0,0,0}; // 9 байт, считанных с DS18B20, или так называемый "блокнот" uint8_t Presense_errors = 0; // Счётчик ошибок - инициализация DS18B20 uint8_t Short_circuit_errors = 0; // Счётчик ошибок - КЗ линии данных DS18B20 int8_t Temperature = 0; // Температура преобразованная (целая часть градусов)
// Прерывание по переполнению таймера 1 (динамическая индикация) ISR (TIMER1_OVF_vect) { /////////////////////////////////// // Индикатор с общим анодом (ОА) /////////////////////////////////// // Выключаю все сегменты #ifndef LEDS_OK LED_SEG_PORT = ~(0b00000000);
// Деактивирую все знакоместа LED_DIG_PORT &= ~((1<<LED_DIG_1A)|(1<<LED_DIG_1B)|(1<<LED_DIG_2A)|(1<<LED_DIG_2B)|(1<<LED_DIG_3A)|(1<<LED_DIG_3B));
// Отображаю цифру в одном из знакомест switch (digit) { case 0: { LED_DIG_PORT |= (1<<LED_DIG_1A)|(1<<LED_DIG_1B); // Активирую 1 знакоместо LED_SEG_PORT = ~(codes[digit1]); // На сегментах отображаю цифру } break; case 1: { LED_DIG_PORT |= (1<<LED_DIG_2A)|(1<<LED_DIG_2B); // Активирую 2 знакоместо LED_SEG_PORT = ~(codes[digit2]); // На сегментах отображаю цифру } break; case 2: { LED_DIG_PORT |= (1<<LED_DIG_3A)|(1<<LED_DIG_3B); // Активирую 3 знакоместо LED_SEG_PORT = ~(codes[digit3]); // На сегментах отображаю цифру } break; };
/////////////////////////////////// // Индикатор с общим катодом (ОК) /////////////////////////////////// // Выключаю все сегменты #else LED_SEG_PORT = 0b00000000;
// Деактивирую все знакоместа LED_DIG_PORT |= (1<<LED_DIG_1A)|(1<<LED_DIG_1B)|(1<<LED_DIG_2A)|(1<<LED_DIG_2B)|(1<<LED_DIG_3A)|(1<<LED_DIG_3B);
// Отображаю цифру в одном из знакомест switch (digit) { case 0: { LED_DIG_PORT &= ~((1<<LED_DIG_1A)|(1<<LED_DIG_1B)); // Активирую 1 знакоместо LED_SEG_PORT = codes[digit1]; // На сегментах отображаю цифру } break; case 1: { LED_DIG_PORT &= ~((1<<LED_DIG_2A)|(1<<LED_DIG_2B)); // Активирую 2 знакоместо LED_SEG_PORT = codes[digit2]; // На сегментах отображаю цифру } break; case 2: { LED_DIG_PORT &= ~((1<<LED_DIG_3A)|(1<<LED_DIG_3B)); // Активирую 3 знакоместо LED_SEG_PORT = codes[digit3]; // На сегментах отображаю цифру } break; }; #endif
// Готовлюсь к отображению следующего знакоместа if (digit >= 2) digit = 0; else digit++; }
// Инициализирую DS18B20 void DS18B20_init (void) { if ((DS18B20_PIN & (1 << DS18B20)) == 0) Short_circuit_errors++; // Проверяю КЗ линии данных DS18B20_PORT &= ~(1 << DS18B20); // Устанавливаю низкий уровень DS18B20_DDR |= (1 << DS18B20); _delay_us(490); DS18B20_DDR &= ~(1 << DS18B20); _delay_us(68); if ((DS18B20_PIN & (1 << DS18B20)) > 0) Presense_errors++; // Ловлю импульс присутствия датчика // Если датчик не подключен, Presense_errors увеличиваю на 1 _delay_us(422); }
// Функция чтения байта из DS18B20 uint8_t DS18B20_read (void) { uint8_t dat = 0; for (uint8_t i=0; i<8; i++) { DS18B20_DDR |= (1 << DS18B20); _delay_us(2); DS18B20_DDR &= ~(1 << DS18B20); _delay_us(4); dat = dat >> 1; if (DS18B20_PIN & (1 << DS18B20)) { dat |= 0x80; } _delay_us(62); } return dat; }
// Функция чтения "блокнота" из DS18B20 void DS18B20_read_scratchpad (void) { for (uint8_t i=0; i<9; i++) // Считываю 9 байт данных, или так называемый "блокнот" { DS_scratchpad[i] = DS18B20_read(); } }
// Функция записи байта в DS18B20 void DS18B20_write (uint8_t dat) { for (uint8_t i=0; i<8; i++) { DS18B20_DDR |= (1 << DS18B20); _delay_us(2); if (dat & 0x01) { DS18B20_DDR &= ~(1 << DS18B20); } else { DS18B20_DDR |= (1 << DS18B20); } dat = dat >> 1; _delay_us(62); DS18B20_DDR &= ~(1 << DS18B20); _delay_us(2); } }
// Алгоритм для вычисления CRC-8 для DALLAS. // Для первого байта CRC = 0, для остальных - то, что получилось от предыдущего. // В расчёт CRC входят первые 8 байт DS18B20. // 1. Нахождение логического исключающего ИЛИ между младшим битом CRC и младшим битом данных. // 2. Если результат равен 0, то: // - Сдвиг вправо CRC. // 3. Если результат равен 1, то: // - Поиск нового значения CRC путем вычисления логического исключающего ИЛИ между CRC и полиномом CRC. // - Сдвиг вправо CRC. // - Установка старшего бита CRC в 1. // - Сдвиг вправо данных. // - Повтор данной последовательности 8 раз на 1 байт данных. uint8_t DS18B20_crc (uint16_t adress) { uint8_t crc = 0; // Переменная для накопления CRC for (uint8_t i=0; i<8; i++) // Считаю CRC 8-ми байт, 9-й байт это CRC { // Расчитываю CRC одного байта crc = crc ^ (*(uint16_t*)(adress+i)); for (uint8_t j=0; j<8; j++) { if (crc & 0x01) crc = (crc >> 1) ^ 0x8C; else crc >>= 1; } } return crc; // Возвращаю CRC }
//Прерывание по сравнению таймера 1 гасим все разряды ISR (TIMER1_COMPA_vect) { /////////////////////////////////// // Индикатор с общим анодом (ОА) /////////////////////////////////// #ifndef LEDS_OK LED_DIG_PORT &= ~((1<<LED_DIG_1A)|(1<<LED_DIG_1B)|(1<<LED_DIG_2A)|(1<<LED_DIG_2B)|(1<<LED_DIG_3A)|(1<<LED_DIG_3B)); /////////////////////////////////// // Индикатор с общим катодом (ОК) /////////////////////////////////// #else LED_DIG_PORT |= (1<<LED_DIG_1A)|(1<<LED_DIG_1B)|(1<<LED_DIG_2A)|(1<<LED_DIG_2B)|(1<<LED_DIG_3A)|(1<<LED_DIG_3B); #endif }
// Главная программа int main (void) { // Настраиваю ножки МК для сегментов LED_SEG_DDR |= 0xFF; LED_SEG_PORT |= 0x00;
// При старте термометра делаю тест всех сегментов 0.5 секунды digit1 = SEG_ALL; digit2 = SEG_ALL; digit3 = SEG_ALL;
while(1) { DS18B20_init(); // Инициализирую DS18B20 DS18B20_write(0xCC); // Пропускаю проверку серийного номера DS18B20 DS18B20_write(0x44); // Запускаю температурное преобразование _delay_ms(1000); // Жду окончания температурного преобразования DS18B20_init(); // Инициализирую DS18B20 DS18B20_write(0xCC); // Пропускаю проверку серийного номера DS18B20 DS18B20_write(0xBE); // Команда на чтение содержимого ОЗУ DS18B20_read_scratchpad(); // Считываю "блокнот" if (Presense_errors | Short_circuit_errors) // Если датчик не отвечает на Presense-импульс, либо КЗ линии данных { // Cтавлю "---" во всех разрядах семисегментного индикатора digit1 = SEG_MINUS; digit2 = SEG_MINUS; digit3 = SEG_MINUS; } else { if (DS_scratchpad[8] == DS18B20_crc(&DS_scratchpad[0])) // Если принятая CRC совпала с расчётной CRC { Temperature = (int8_t)((DS_scratchpad[1] << 4)|(DS_scratchpad[0] >> 4)); // Совмещаю младший и старший байты, отбрасываю дробную часть, получаю температуру в градусах.
if (Temperature < 0) // Отрицательная температура { Temperature =-Temperature; // Перевожу отрицательное число в положительное digit1 = SEG_MINUS; // Символ "-" if (Temperature < 10) { // -9*...-1* digit2 = Temperature % 10; // Единицы градусов digit3 = SEG_DEGREE; // Символ градуса } else { // -55...-10 digit2 = Temperature % 100 / 10; // Десятки градусов digit3 = Temperature % 10; // Единицы градусов } } else // Положительная температура { if (Temperature > 99) { // 100...125 digit1 = Temperature % 1000 / 100; // Сотни градусов digit2 = Temperature % 100 / 10; // Десятки градусов digit3 = Temperature % 10; // Единицы градусов } else { if (Temperature > 9) { // 10*...99* digit1 = Temperature % 100 / 10; // Десятки градусов digit2 = Temperature % 10; // Единицы градусов digit3 = SEG_DEGREE; // Символ градуса } else { // 0*...9* digit1 = SEG_BLANK; // Пустой символ digit2 = Temperature % 10; // Единицы градусов digit3 = SEG_DEGREE; // Символ градуса } } } } else // Если принятая CRC не совпала с расчётной, выводим надпись "crc" на семисегментный индикатор { digit1 = SEG_C; // "c" digit2 = SEG_R; // "r" digit3 = SEG_C; // "c" } } Presense_errors=Short_circuit_errors=0; // Сбрасываю счётчик ошибок инициализации DS18B20, сбрасываю флаг КЗ } }
Разобрался. Я моделировал в Proteus, а в нём почему то ШИМ не работает, возможно это связано с тем, что семисегментные индикаторы в нём не умеют менять яркость. Хотя на осциллографе должна изменяться скважность импульсов подаваемых на катоды/аноды, но почему то и этого не происходит. В общем Proteus для моделирования таких задач не подходит. На реальном железе всё работает, как нельзя лучше. Яркость изменяется в зависимости от значения регистра OCR1A. Чем оно выше, тем выше яркость.
Добрый день, на вход функции надо подать число в формате Q15, это число с фиксированной точкой где 1бит отводится под целую часть и 15 под дробную часть. У меня же данные, которые хотелось бы подать хранятся в uint16_t. Нужно ли как то преобразовывать uint16_t или можно подать так? Мне думается, что можно подать без каких-либо преобразований uint16_t? Подскажите пожалуйста как правильно?
если сами биты в вашем числе расположены в соответствии с требованиями функции, то можно подать. для исключения предупреждения компилятора при подаче рекомендую принудительно привести число к требуемому типу.
как-то так: my_func((Q15)my_int16_var);
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Не уверен, что это так, но очень похоже на формат двоичного числа с фиксированной точкой, где в таблице указываетя количество бит до десятичной точки и количество бит после нее. То есть, в последней колонке указано, на сколько бит вправо смещена десятичная точка, относительно формата во второй колонке.
_________________ Не ошибается только то, кто ничего не делает. Тот, кто признает свои ошибки, на них учится. Глупец же, упорствуя в своих заблуждениях, остается глупцом.
Други-знатоки, помогите, если не трудно, разобраться в указателях
По определению указатель есть переменная, которая содержит в себе адрес другой переменной. Определение в общем понятно. Непонятно следующее: если он (указатель) содержит адрес, который обычно не занимает меньше 16 бит, то почему его всегда ориентируют на размерность переменной, на которую он указывает? Если, скажем, имеется массив char array[10], то почему указатель на него *ptr тоже должен быть типа char? Ведь адрес, по которому размещается массив, скорее всего не менее 16 бит?
_________________ Каждый имеет право на свое личное ошибочное мнение.
У меня было тяжелое детство - я до 14 лет смотрел черно-белый телевизор.
если он (указатель) содержит адрес, который обычно не занимает меньше 16 бит, то почему его всегда ориентируют на размерность переменной, на которую он указывает?
Ничего подобного, Вы немного запутались. Все указатели, на любые типы, имеют один и тот же размер.
Цитата:
Если, скажем, имеется массив char array[10], то почему указатель на него *ptr тоже должен быть типа char?
Не так. Правильно не "указатель *ptr типа char", а "указатель ptr типа char*".
В данном случае, например, если вы пишете что-то вроде
Код:
char array[10]; char *ptr = array;
То указатель - это ptr. Не *ptr (это, вообще-то, разыменование указателя - т.е., содержимое ячейки, куда он указывает), а именно ptr. И тип у него - char* (указывающий на char).
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения