Начал недавно изучать программирование AVR на Си. Поднакопил немного знаний и решил сделать свое первое устройство, нагрузку для разряда АКБ.
Набросал в протеусе вот такую схемку
[img][url=https://img.radiokot.ru/files/158040/medium/3kcp23hfco.jpg][/img]
Написал в AtmelStudio вот такой код
Спойлер
Код: Выделить всё
#define F_CPU 8000000UL //частота МК
#include <avr/io.h>
#include <util/delay.h> //библиотека задержек
#include <avr/interrupt.h> //библиотека прерываний
//-----------------------переменные динамической индикации------------------------------
unsigned char n = 0; //переменная количества вхождений в прерывание
unsigned char r1_1000 = 0; //переменная значения разряда тысяч
unsigned char r2_100 = 0; //переменная значения разряда сотен
unsigned char r3_10 = 0; //переменная значения разряда десятков
unsigned char r4_1 = 0; //переменная значения разряда единиц
//--------------------------------------------------------------------------------------
//-----------------------переменные времени---------------------------------------------
unsigned char sec = 0; //переменная количества секунд
unsigned char min = 0; //переменная количества минут
unsigned char hour = 0; //переменная количества часов
//--------------------------------------------------------------------------------------
//-----------------------переменные кнопок (внешние прерывания)-------------------------
unsigned char button_1 = 0; //переменная количества секунд
unsigned char button_2 = 0; //переменная количества минут
//--------------------------------------------------------------------------------------
unsigned int temp = 0; //переменная хранения значения температуры
float volt = 0; //переменная хранения значения напряжения АКБ
unsigned char n_ADC = 0; //переменная количества преобразований АЦП
//-----------------------динамическая индикация и отображение информации----------------
void LED_DISP_pin_setup(void) //функция настройки пинов LED индикатора
{
//настройка сегментов A, B, Dp, C, G
DDRB |= (1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0); //режим работы - выход
PORTB &= ~((1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0)); //низкий потенциал на пинах
//настройка сегментов E, D, F
DDRD |= (1<<7) | (1<<6) | (1<<5); //режим работы - выход
PORTD &= ~((1<<7) | (1<<6) | (1<<5)); //низкий потенциал на пинах
//настройка разрядов 1-4
DDRC |= (1<<3) | (1<<2) | (1<<1) | (1<<0); //режим работы - выход
PORTC |= (1<<3) | (1<<2) | (1<<1) | (1<<0); //высокий потенциал на пинах
}
void Timer0_setup(void) //функция настройки нулевого таймер-счетчика
{
//включение таймер-счетчика с делителем на 64
TCCR0 |= (1<<CS01) | (1<<CS00);
TCCR0 &= ~(1<<CS02);
//настройка прерывания по переполнению счетого регистра
TIMSK |= (1<<TOIE0);
//обнуление счетного регистра
TCNT0 = 0;
}
void show_volt(unsigned int znach_volt)
{
r1_1000 = znach_volt / 1000; //определение значения десятков вольт
r2_100 = znach_volt % 1000 / 100; //определение значения единиц вольт
r3_10 = znach_volt % 100 / 10; //определение значения десятоых долей вольта
r4_1 = 12; //знак напряжения
}
void show_temp(unsigned int znach_temp)
{
r1_1000 = znach_temp / 10; //
r2_100 = znach_temp % 10; //
r3_10 = 10;
r4_1 = 11;
}
void show_min_sec(unsigned int znach_min, unsigned char znach_sec)
{
r1_1000 = znach_min / 10; //определение значения десятков минут
r2_100 = znach_min % 10; //определение значения единиц минут
r3_10 = znach_sec / 10; //определение значения десятков секунд
r4_1 = znach_sec % 10; //определение значения единиц секунд
}
void show_hour_min(unsigned int znach_hour, unsigned char znach_min)
{
r1_1000 = znach_hour / 10; //определение значения десятков часов
r2_100 = znach_hour % 10; //определение значения единиц часов
r3_10 = znach_min / 10; //определение значения десятков минут
r4_1 = znach_min % 10; //определение значения единиц минут
}
void digits(unsigned char n) //функция отображения цифр
{
//выключаем сегменты
PORTB &= ~((1<<4) | (1<<3) | (1<<2) | (1<<1) | (1<<0)); //низкий потенциал на пинах
PORTD &= ~((1<<7) | (1<<6) | (1<<5)); //низкий потенциал на пинах
switch(n)
{
case 0: PORTB |= (1<<3) | (1<<1) | (1<<0);
PORTD |= (1<<7) | (1<<6) | (1<<5);
break;
case 1: PORTB |= (1<<3) | (1<<1);
break;
case 2: PORTB |= (1<<4) | (1<<1) | (1<<0);
PORTD |= (1<<6) | (1<<5);
break;
case 3: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
PORTD |= (1<<6);
break;
case 4: PORTB |= (1<<4) | (1<<3) | (1<<1);
PORTD |= (1<<7);
break;
case 5: PORTB |= (1<<4) | (1<<3) | (1<<0);
PORTD |= (1<<7) | (1<<6);
break;
case 6: PORTB |= (1<<4) | (1<<3) | (1<<0);
PORTD |= (1<<7) | (1<<6) | (1<<5);
break;
case 7: PORTB |= (1<<3) | (1<<1) | (1<<0);
break;
case 8: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
PORTD |= (1<<7) | (1<<6) | (1<<5);
break;
case 9: PORTB |= (1<<4) | (1<<3) | (1<<1) | (1<<0);
PORTD |= (1<<7) | (1<<6);
break;
case 10: PORTB |= (1<<4) | (1<<1) | (1<<0); //знак градуса
PORTD |= (1<<7);
break;
case 11: PORTB |= (1<<0); //знак цельсия
PORTD |= (1<<7) | (1<<6) | (1<<5);
break;
case 12: PORTB |= (1<<3) | (1<<1); //знак напряжения
PORTD |= (1<<7) | (1<<6) | (1<<5);
break;
}
}
ISR(TIMER0_OVF_vect) //макрос обработки прерывания нулевого таймер-счтечика
{
if(++n > 4) n = 1;
if (n == 1) //отображение в первом разряде
{
PORTC |= (1<<3) | (1<<2) | (1<<1);
PORTC &= ~(1<<0);
digits(r1_1000);
}
else if (n == 2) //отображение во втором разряде
{
PORTC |= (1<<3) | (1<<2) | (1<<0);
PORTC &= ~(1<<1);
digits(r2_100);
}
else if (n == 3) //отображение в третьем разряде
{
PORTC |= (1<<3) | (1<<1) | (1<<0);
PORTC &= ~(1<<2);
digits(r3_10);
}
else if (n == 4) //отображение в четвертом разряде
{
PORTC |= (1<<2) | (1<<1) | (1<<0);
PORTC &= ~(1<<3);
digits(r4_1);
}
}
//--------------------------------------------------------------------------------------
//----------------отсчет одной секунды--------------------------------------------------
void Timer1_setup(void)
{
//включение таймер-счетчика с делителем на 256
TCCR1B |= (1<<CS12);
TCCR1B &= ~((1<<CS11) | (1<<CS10));
//разрешаем прерывание по совпадению
TIMSK |= (1<<OCIE1A);
//разрешаем автоматическое обнуление счетного регистра при совпадении
TCCR1B |= (1<<WGM12);
//обнуляем счетный регистр
TCNT1 = 0;
//записываем в счетный регистр значение кличества тактов равное одной секунде
OCR1A = 31250;
}
ISR(TIMER1_COMPA_vect)
{
sec++;
if (sec == 60)
{
sec = 0;
min++;
}
else if (min == 60)
{
min = 0;
hour++;
}
}
//--------------------------------------------------------------------------------------
//----------------внешние прерывания----------------------------------------------------
void External_interrupts_pin_setup(void)
{
DDRD &= ~((1<<3) | (1<<2));
PORTD |= (1<<3) | (1<<2);
}
void External_interrupts_setup(void)
{
//настройка прерывания INT0 по спаду
MCUCR |= (1<<ISC01);
MCUCR &= ~(1<<ISC00);
//настройка прерывания INT1 по спаду
MCUCR |= (1<<ISC11);
MCUCR &= ~(1<<ISC10);
//разрешаем внешние прерывания INT0 и INT1
GICR |= (1<<INT1) | (1<<INT0);
}
ISR(INT0_vect) //макрос обработки прерывания, изменение отображаемой информации
{
if (++button_1 > 3) button_1 = 1;
}
ISR(INT1_vect)
{
}
//--------------------------------------------------------------------------------------
//-------------------------------АЦП----------------------------------------------------
void ADC_pin_setup(void)
{
DDRC &= ~((1<<5) | (1<<4)); //пин PD4 и PD5 в режим работы вход
PORTC &= ~((1<<5) | (1<<4)); //высокое входное сопротивление на пинах PD4 и PD5
}
void ADC_setup()
{
//разрешаем работу АЦП
ADCSRA |= (1<<ADEN);
//непрерывный режим работы АЦП
ADCSRA |= (1<<ADFR);
//частота дискретизации 125 кГц (частота МК 8 МГЦ, делитель 64)
ADCSRA |= (1<<ADPS2) | (1<<ADPS1);
ADCSRA &= ~(1<<ADPS0);
//подключаем внешний ИОН - 5В
//ADMUX |= (1<<REFS0);
//ADMUX &= ~(1<<REFS1);
//подключаем внутренний ИОН - 2,56В
ADMUX |= (1<<REFS1) | (1<<REFS0);
//выравнивание результата преобразования в регистре ADC по правой стороне
ADMUX &= ~(1<<ADLAR);
//настраиваем 5-й канал АЦП
ADMUX |= (1<<MUX2) | (1<<MUX0);
ADMUX &= ~((1<<MUX3) | (1<<MUX1));
//разрешаем преобразование АЦП
ADCSRA |= (1<<ADSC);
//разрешаем прерывание от АЦП
ADCSRA |= (1<<ADIE);
}
void ADC_set_chanel(unsigned char chanel) //функция выбора канала АЦП
{
switch(chanel)
{
case 0: //4-й канал
ADMUX |= (1<<MUX2);
ADMUX &= ~((MUX3) | (MUX1) | (MUX0));
break;
case 1: //5-й канал
ADMUX |= (1<<MUX2) | (1<<MUX0);
ADMUX &= ~((1<<MUX3) | (1<<MUX1));
}
}
ISR(ADC_vect)
{
n_ADC++; //этотвариант работает
if (n_ADC == 1)
{
temp = (ADC*2.56/1024)*100;
}
else if (n_ADC == 2)
{
volt = (ADC*2.56/1024.0*5.3)*100;
n_ADC = 0;
}
ADC_set_chanel(n_ADC);
}
//--------------------------------------------------------------------------------------
int main(void)
{
LED_DISP_pin_setup();
Timer0_setup();
Timer1_setup();
External_interrupts_pin_setup();
External_interrupts_setup();
ADC_pin_setup();
ADC_setup();
sei(); //глобальное разрешение прерываний
while (1)
{
if (button_1 == 1)
{
show_min_sec(min, sec);
}
else if (button_1 == 2)
{
show_temp(temp);
}
else if (button_1 == 3)
{
show_volt(volt);
}
}
}
И вот тут возник вопрос. А как включить десятичную точку во втором разряде при отображении напряжения?