Обсуждаем контроллеры компании Atmel.
Ответить

Re: ATmega + 12bit ADC

Пн сен 06, 2021 07:43:09

пересматривать надо не диапазон коррекции, а сам алгоритм.
нужно разделять режим установки уставки тока и рабочий режим.
а то получается фактически медленное ("единичка в секунду") накручивание тока.
нафига в таком случае вообще сдались эти всякие супер-пупер 15-бит, погрешности и тд...
крути ручку тока как попало да и все, эффект будет тот же
Последний раз редактировалось slav0n Пн сен 06, 2021 08:17:41, всего редактировалось 1 раз.

Re: ATmega + 12bit ADC

Пн сен 06, 2021 08:17:00

установка задания и рабочий режим у меня разделены.
устанавливать надо только в режиме "стоп". рабочем режиме крутить ручку бесполезно, так как после пуска запоминается установленное значение.
ты будешь делать, как ты посчитаешь нужным. а мне нравится так, как сделал я.

Re: ATmega + 12bit ADC

Пн сен 06, 2021 08:28:03

Starichok51 писал(а):а если автокоррекция успела набрать, допустим, код для 4,6А, а нагрузка захотела взять больше 4,6А (или сделали кз), то в первый момент ток будет 4,6А. и тут же коррекция начнет снижать ток к установленному значению 3А.
ну, если тебе нравится такая фигня, то зачем оно другим?

Добавлено after 7 minutes 8 seconds:
допустим, наивный юзер ставит ограничение 0.1 А в надежде уберечь свое детище от вони-огони.
ну, значит, он в нем ковыряется-настраивает... время идет, все норм...
тут чувак решает зафорсить режим своей приблуды надеясь на ограничение 0.1А... а твой супер-БП ему куяк, и десяточку ампер в "первый момент"

Re: ATmega + 12bit ADC

Пн сен 06, 2021 08:41:57

не в начальный момент, а минут через 15 ...
и по поводу десяточки Ампер я уже согласился с Димой, что нужно ограничить коррекцию 10-15 единицами, чтобы далеко не убежало. или ты этого не читал?

Добавлено after 1 minute 28 seconds:
а по поводу других - еще никто не пожаловался из тех, кто собрал мой проект.

Re: ATmega + 12bit ADC

Пн сен 06, 2021 10:52:55

Starichok51 писал(а): нужно ограничить коррекцию 10-15 единицами, чтобы далеко не убежало
это костыли для кривого алгоритма
правильный алгоритм никуда убегать не должен

Добавлено after 1 hour 11 minutes 10 seconds:
Starichok51 писал(а):установка задания и рабочий режим у меня разделены.
устанавливать надо только в режиме "стоп". рабочем режиме крутить ручку бесполезно, так как после пуска запоминается установленное значение.
а крутить ручку физически нет необходимости, "крутит ручку" твой чудо-алгоритм

Добавлено after 56 minutes 48 seconds:
вести с полей оверсемплинга
Код:
#define ROUNDS   (1024 * 2)
. . .
res += ((adc1 * 4550.0 / 102.4 / ROUNDS) - res) * 0.125;
вижу на дисплее разрешение 100 мкВ, но уже начинает возникать резонный вопрос - а нафига?

Re: ATmega + 12bit ADC

Пн сен 06, 2021 17:55:48

slav0n писал(а):а нафига?
для тока самый раз, максимум 4,550А, разрешение 0,1миллиА.
Другой вопрос, а ты с чем сравниваешь показания, какой образцовый прибор используешь?

Re: ATmega + 12bit ADC

Пн сен 06, 2021 18:24:31

я пишу не про абсолютное значение, а про разрешение
т.е. с каким шагом изменяются показания

Re: ATmega + 12bit ADC

Пн сен 06, 2021 18:43:01

тогда расскажи, чему у тебя равен шаг и чему равно разрешение.

Re: ATmega + 12bit ADC

Пн сен 06, 2021 19:43:37

ну, так я же кино показывал с шагом 1 мВ
100 мкВ для реального применения неинтересен, слишком инерционный фильтр получается. оно чисто для спорта
1 десятичный порядок по идее требует как минимум 3 двоичных
Последний раз редактировалось slav0n Пн сен 06, 2021 19:49:17, всего редактировалось 1 раз.

Re: ATmega + 12bit ADC

Пн сен 06, 2021 19:46:22

Меня интересует максимум и шаг:
максимум 5В шаг 1мВ
максимум 50В шаг 10мВ
максимум 500В шаг 100мВ

Re: ATmega + 12bit ADC

Пн сен 06, 2021 19:50:50

так все это уже давно есть
максимум 5В шаг 1мВ - напрямую с опорой 5в
максимум 50В шаг 10мВ - через делитель 1/10
максимум 500В шаг 100мВ - делитель 1/100

Re: ATmega + 12bit ADC

Сб сен 11, 2021 08:39:41

избавился от плавающей точки, а это минус 300Б кода.
Код:
#define ROUNDS   1024
. . .
res1 += (((adc1 >> 5) * 4550 / 1024 / (ROUNDS >> 5)) - res1) >> 2;

Re: ATmega + 12bit ADC

Вт сен 26, 2023 15:38:49

желаю здравия :beer: churchyard in the air. спасибо всем неравнодушным, кто высказывал свои гипотезы.
вот такая кодировка использовалась для одной из двухканальных головок.
Спойлер
Код:
[code]//ATmega_8A + max7219 + mcp3201_0 + mcp3201_1
//va-meter +0...399.99vdc / +0...9.999adc
//16MHz

#define F_CPU 16000000UL                                     //тактовая частота мк (unsigned long)
#include <avr/io.h>                                          //подключение библиотеки "ввод/вывод" мк
#include <util/delay.h>                                      //подключение библиотеки "пауза" мк
#include <avr/interrupt.h>                                   //подключение библиотеки "прерывание" мк

#define Vref 4.096 //+Vref=4.096vdc
#define Kv 4.195 //коэффициент преобразования для вычислнения величины [V] Kv=4096*4.096/3999
#define Ki 1.678 //коэффициент преобразования для вычислнения величины [I] Ki=4096*4.096/9999
#define ADC_CS0_0 PORTB &= ~(1<<PORTB0) //ADC_CS0=0
#define ADC_CS0_1 PORTB |= (1<<PORTB0) //ADC_CS0=1
#define ADC_CS1_0 PORTB &= ~(1<<PORTB2) //ADC_CS1=0
#define ADC_CS1_1 PORTB |= (1<<PORTB2) //ADC_CS1=1
#define ADC_CLK_0 PORTB &= ~(1<<PORTB5) //ADC_CLK=0
#define ADC_CLK_1 PORTB |= (1<<PORTB5) //ADC_CLK=1

#define DRV_CLK_0 PORTC &= ~(1<<PORTC0) //DRV_CLK=0
#define DRV_CLK_1 PORTC |= (1<<PORTC0) //DRV_CLK=1
#define DRV_CS_0 PORTC &= ~(1<<PORTC1) //DRV_CS=0
#define DRV_CS_1 PORTC |= (1<<PORTC1) //DRV_CS=1
#define DRV_MOSI_0 PORTC &= ~(1<<PORTC2) //DRV_MOSI=0
#define DRV_MOSI_1 PORTC |= (1<<PORTC2) //DRV_MOSI=1
#define measure 0x01 // по результатам 1 измерений вычислить средне-арифметическое значение (VOLn=1+...+VOLn=1)/1

//----------
unsigned int Digit[8]; //массив беззнаковых целочисленных переменных Digit из 8 переменных (8 разрядов драйвера 7seg LED-дисплея)
unsigned char DRV_MOSI[8]; //массив беззнаковых однобайтных символьных переменных DRV_MOSI из 8 переменных (адреса и команды для конфигурирования драйвера)
unsigned int Display2_ADC; //беззнаковая целочисленная переменная Display2_ADC
unsigned char V; //беззнаковая однобайтная символьная переменная V (значение регистра ацп с результатом оцифровки напряжения)
unsigned char I; //беззнаковая однобайтная символьная переменная I (значение регистра ацп с результатом оцифровки тока)
unsigned int adc_conv count; // переменная для задержки обновления данных на индикаторах

unsigned int dt_v; //12-битный результат оцифровки величины [V]
float Av; //Av=dt_v
float Mv; //окончательный результат работы сглаживающего фильтра в канале [V]
float Mv1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [V]
float Ks_v = 0.05; //коэффициент сглаживания в канале [V]

unsigned int dt_i; //12-битный результат оцифровки величины [I]
float Ai; //Ai=dt_i
float Mi; //окончательный результат работы сглаживающего фильтра в канале [I]
float Mi1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [I]
float Ks_i = 0.05; //коэффициент сглаживания в канале [I]

//--- инициализация CS0 ---
void CS0_ini(void) //функция инициализации порта PB0
{
   DDRB |= (1<<PORTB0); //PB0 на вывод
   PORTB |= (1<<PORTB0); //PB0_hi
}

//--- инициализация CS1 ---
void CS1_ini(void) //функция инициализации порта PB2
{
   DDRB |= (1<<PORTB2); //PB2 на вывод
   PORTB |= (1<<PORTB2); //PB2_hi
}

//--- инициализация генератора ---
void PB1_ini(void) //функция инициализации порта PB1
{
   DDRB |= (1<<PORTB1); //PB1 на вывод (oc1a)
   PORTB &= ~(1<<PORTB1); //PB1 сброс
}

//--- инициализация PB2 ---
/*void PB2_ini(void) //функция инициализации порта PB2 (выход запуска генератора +Vpwr = +9...12v)
{
   DDRB |= (1<<PORTB2); //PB2 на вывод (oc1b)
   PORTB &= ~(1<<PORTB2); //PB2 сброс
}*/

//--- инициализация таймера oc1a ---
void oc1a(void) //функция таймера
{
   ASSR=0x00; //сбрасываем полностью регистр assr
   TCCR1A |= ((1<<COM1A1)|(1<<WGM10)); //Fast PWM oc1a, Clear oc1a on Compare Match, clkT2S/1 (no prescalling)
   TCCR1B |= ((1<<WGM12)|(1<<CS10));
   TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 1
   TCNT1L=0x00;
   OCR1AH=0x00;
   OCR1AL=0x55; //Output Compare Register = dec85 - заполнение шим +DC~33%
   TIMSK=0x00; //сброс регистра timsk
}

//--- инициализация таймера oc1b ---
/*void oc1b(void) //функция таймера
{
   ASSR=0x00; //сбрасываем полностью регистр assr
   TCCR1A |= ((1<<COM1B1)|(1<<WGM10)); //Fast PWM oc1b, Clear oc1b on Compare Match, clkT2S/1 (no prescalling)
   TCCR1B |= ((1<<WGM12)|(1<<CS10));
   TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 2
   TCNT1L=0x00;
   OCR1BH=0x00;
   OCR1BL=0x64; //Output Compare Register = dec100 - заполнение шим +DC~40%
   TIMSK=0x00; //сброс регистра timsk
}*/

//--- инициализация шины SPI_ADC ---
void ADC_SPI_ini(void)
{
   DDRB &= ~(1<<PORTB4); PORTB |= (1<<PORTB4); //MISO, pull-up=ON
   DDRB |= ((1<<PORTB5)|(1<<PORTB2)|(1<<PORTB0)); //выход ADC_CLK, выход ADC_CS1, выход ADC_CS0
   PORTB |= ((1<<PORTB2)|(1<<PORTB0));
   PORTB &= ~(1<<PORTB5); //сброс шины SPI_ADC
   SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)); //включим шину SPI, объ§вим ведущим, SCK=16e+06/128=125kHz
}

//--- функция передачи/приёма данных по шине SPI ---
void SPI_SendByte(char byte)
{
   SPDR = byte; //
   while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤
}
unsigned char SPI_ChangeByte(char byte)
{
   SPDR = byte;
   while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤ (обмен¤ютс¤)
   return SPDR;
}

//--- функция опроса внешнего АЦП mcp3201_0 ---
unsigned int Read_3201_0(unsigned char channel)
{
   unsigned int b1,b2;
   ADC_CS0_0; //CS0=0
   b1=SPI_ChangeByte(0); //первый байт
   b2=SPI_ChangeByte(0); //второй байт
   b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
   b1<<=3;
   b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
   ADC_CS0_1; //CS0=1
   return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- функция опроса внешнего АЦП mcp3201_1 ---
unsigned int Read_3201_1(unsigned char channel)
{
   unsigned int b1,b2;
   ADC_CS1_0; //CS1=0
   b1=SPI_ChangeByte(0); //первый байт
   b2=SPI_ChangeByte(0); //второй байт
   b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
   b1<<=3;
   b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
   ADC_CS1_1; //CS1=1
   return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- инициализация шины данных SPI_DRV драйвера max7219 ---
void DRV_SPI_ini(void)
{
   DDRC = 0x07; //биты PС0-PС2 порта PС на вывод
   PORTC &= ~((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0-PС2 сброс
}

//--- функция побитовой отправки данных в драйвер max7219 ---
void Send_max7219(unsigned char rg, unsigned char dt) //
{
   unsigned char rg_copy; //копия значения переменной rg
   unsigned char i; //переменная для побитной отправки данных в драйвер индикаторов
   
   DRV_MOSI[rg] = dt; //
   rg_copy = rg; //создадим копию значения переменной rg
   
   DRV_CS_0; //отправим «0» на вывод CS микросхемы MAX7219, чтобы начать процесс передачи адреса и данных
   asm("nop"); //пауза в 1 такт
   
   for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта адреса в микросхему MAX7219
   {
      if((rg & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
      {
         DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
      }
      else //если же rg * 0b1000 0000 = 0, ...
      {
         DRV_MOSI_0; //...отправим 0 на вывод Din микросхемы MAX7219
      }
      
      //создадим тактовый импульс на выводе CLK микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
      rg <<= 1; //сдвинем значение переменной rg на 1 бит влево
   } //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта адреса в микросхему MAX7219 будет окончена
   
   for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта данных в микросхему MAX7219
   {
      if((DRV_MOSI[rg_copy] & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
      {
         DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
      }
      else //если же rg * 0b1000 0000 = 0, ...
      {
         DRV_MOSI_0; //отправим 0 на вывод Din микросхемы MAX7219
      }
      
      //создадим тактовый импульс на выводе CLK микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
      DRV_MOSI[rg_copy] <<= 1; //сдвинем значение переменной DRV_MOSI на 1 бит влево
   }
   //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта данных в микросхему MAX7219 будет окончена
   DRV_CS_1; //отправим «1» на вывод CS микросхемы MAX7219, чтобы завершить процесс передачи адреса и данных
}

//--- инициализация драйвера max7219 ---
void MAX7219_ini(void)
{
   Send_max7219(0x09,0xFF); //(номер регистра, данные) включаем режим BCD code B, для 0-7 разрядов
   Send_max7219(0x0A,0x0A); //DC = 21/32 яркость свечения
   Send_max7219(0x0B,0x07); //число используемых разрядов (0-7 разрядов)
   Send_max7219(0x0C,0x01); //отключаем режим энергосбережения (Shutdown)
   
   for(V=1;V<9;V++)
   {
     Send_max7219(V,0x0F);
   }   
}

//--- Функция вывода значений на индикатор вольтметра ---
void ledprint_1(unsigned int number)
{
   Digit[8]=number/1000 ? number/1000: 0x0F; //гашение незначащего нуля в разряде "тысячи"
   Digit[7]=number/1000 || number%1000/100 ? number%1000/100: 0x0F; //гашение незначащего нуля в разряде "сотни"
   Digit[6]=number%100/10; //десятки
   Digit[6]=Digit[6]|128; //вкл децимальную точку в разряде Digit_6   
   Digit[5]=number%10; //единицы
   
   for(V=5;V<9;V++)
   {
      DRV_MOSI[V] = Digit[V];
      Send_max7219(V,DRV_MOSI[V]);
   }
}

//--- функция вывода значений на индикатор амперметра ---
void ledprint_2(unsigned int number)
{
   Digit[4]=number/1000; //тысячи
   Digit[4]=Digit[4]|128; //вкл децимальную точку в разряде Digit_4
   Digit[3]=number%1000/100; //сотни
   Digit[2]=number%100/10; //десятки
   Digit[1]=number%10; //единицы
   
   for(I=1;I<5;I++)
   {
      DRV_MOSI[I] = Digit[I];
      Send_max7219(I,DRV_MOSI[I]);
   }
}

//--- функция оцифровки напряжения ---
float ADCV_Conv(unsigned int dt_v)
{
   float dt_v1; //тип данных с плавающей точкой
   dt_v1=((float)dt_v*(Vref))/Kv; //преобразование 12-битного числа типа float в величину измеренного напряжения
   unsigned int adcv_tmp = 0; //сброс переменной для хранения промежуточных результатов оцифровки
   unsigned char adcv_counter = 0; //сброс переменной усреднения оцифровки measure
   if(adcv_counter < measure)
   {
      adcv_tmp += dt_v1; //adcv_tmp = adcv_tmp + dt_v1
      adcv_counter ++;
   }
   else
   {
      dt_v1 = adcv_tmp >> 0; //adcv_tmp / measure
      adcv_counter = 0; //сброс счётчика усреднения
      adcv_tmp = 0; //сброс регистра промежуточных результатов оцифровки
   }
   return dt_v1; //возвращаем величину измеренного напряжения в вольтах [V]
}

//--- функция оцифровки тока ---
float ADCI_Conv(unsigned int dt_i)
{
   float dt_i1; //тип данных с плавающей точкой
   dt_i1=((float)dt_i*(Vref))/Ki; //преобразование 12-битного числа типа float в величину измеренного тока
   unsigned int adci_tmp = 0; //сброс переменной для хранения промежуточных результатов оцифровки
   unsigned char adci_counter = 0; //сброс переменной усреднения оцифровки measure
   if(adci_counter < measure) //пока число в счётчике измерений меньше предельного количества измерений  measure...
   { //...суммируем результаты оцифровки в регистре промежуточных результатов
      adci_tmp += dt_i1; //adci_tmp = adci_tmp + dt_i1
      adci_counter ++; //...продолжаем инкрементировать счётчик измерений
   }
   else //если же счётчик накопил предельное кол-во измерений...
   { //...то выходим из цикла накопления в регистре и находим среднее арифметическое...
      dt_i1 = adci_tmp >> 0; //...adci_tmp / measure (сдвигаем данные в регистре накопления на 5 разрядов вправо, что равнозначно /2^5=32 )
      adci_counter = 0; //сброс счётчика усреднения
      adci_tmp = 0; //сброс регистра промежуточных результатов оцифровки
   }
   return dt_i1; //возвращаем величину измеренного тока в амперах [A]
}

//--- основная функция с бесконечным циклом ---
int main(void)
{
   float dt_v=0; //сброс на ноль SPDR
   float dt_i=0; //сброс на ноль SPDR
   CS0_ini(); //инициализация порта PB0
   CS1_ini(); //инициализация порта PB2
   PB1_ini(); //инициализация порта PB1
   //PB2_ini(); //инициализация порта PB2
   oc1a(); //инициализация генератора
   OCR1AH = 0x00; //запись в регистр сравнения ocr1a...
   OCR1AL = 0x55; //...числа dec85
   //oc1b(); //инициализация инвертора +5v >> +9...12v
   //OCR1BH = 0x00; //запись в регистр сравнения ocr1a...
   //OCR1BL = 0x64; //...числа dec100
   ADC_SPI_ini(); //инициализация шины SPI
   DRV_SPI_ini(); //инициализация шины данных драйвера
   MAX7219_ini(); //инициализация MAX7219
   //ADCV_ini(); //инициализация внутреннего АЦП
   //----------
   while(1)
   {
      dt_v = ADCV_Conv(Read_3201_0(0)); //считаем значение SPDR АЦП напряжения
      Av=dt_v; //
      Mv = Ks_v * Av + Mv1 * (1-Ks_v); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
      Mv1=Mv; //      
      dt_i = ADCI_Conv(Read_3201_1(1)); //считаем значение SPDR АЦП тока
      Ai=dt_i; //
      Mi = Ks_i * Ai + Mi1 * (1-Ks_i); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
      Mi1=Mi; //
      
      adc_conv count++;  //инкрементируем счётчик ацп-преобразователя
      if(adc_conv count==1000) // выводим на дисплеи результат только каждого n=1000 преобразования
      {
          adc_conv count=0; //сброс счётчика ацп-преобразований
        ledprint_1(Mv); //отправим значение в функцию вывода на индикатор вольтметра
        ledprint_2(Mi); //отправим значение в функцию вывода на индикатор амперметра      
      }            
   }
}









[spoiler]

Спойлер
Вложения
va_meter_400vdc_10adc_ver_1.pdf
(363.08 KiB) Скачиваний: 15
chopper 48vdc_5vdc.pdf
(409.7 KiB) Скачиваний: 9
chopper 38v_5v0.pdf
(202.06 KiB) Скачиваний: 12

Re: ATmega + 12bit ADC

Ср окт 04, 2023 17:32:34

Спойлер
Код:
//ATmega_8A + max7219 + mcp3201_0 + mcp3201_1
//va-meter +0...409.6vdc / +0...9.999adc
//16MHz

#define F_CPU 16000000UL                                     //тактовая частота мк (unsigned long)
#include <avr/io.h>                                          //подключение библиотеки "ввод/вывод" мк
#include <util/delay.h>                                      //подключение библиотеки "пауза" мк
#include <avr/interrupt.h>                                   //подключение библиотеки "прерывание" мк

#define Vref 4.096 //+Vref=4.096vdc
#define Kv 4.096 //коэффициент преобразования для вычислнения величины [V] Kv=4096*4.096/4096
#define Ki 1.678 //коэффициент преобразования для вычислнения величины [I] Ki=4096*4.096/10 000
#define ADC_CS0_0 PORTB &= ~(1<<PORTB0) //ADC_CS0=0
#define ADC_CS0_1 PORTB |= (1<<PORTB0) //ADC_CS0=1
#define ADC_CS1_0 PORTB &= ~(1<<PORTB2) //ADC_CS1=0
#define ADC_CS1_1 PORTB |= (1<<PORTB2) //ADC_CS1=1
#define ADC_CLK_0 PORTB &= ~(1<<PORTB5) //ADC_CLK=0
#define ADC_CLK_1 PORTB |= (1<<PORTB5) //ADC_CLK=1

#define DRV_CLK_0 PORTC &= ~(1<<PORTC0) //DRV_CLK=0
#define DRV_CLK_1 PORTC |= (1<<PORTC0) //DRV_CLK=1
#define DRV_CS_0 PORTC &= ~(1<<PORTC1) //DRV_CS=0
#define DRV_CS_1 PORTC |= (1<<PORTC1) //DRV_CS=1
#define DRV_MOSI_0 PORTC &= ~(1<<PORTC2) //DRV_MOSI=0
#define DRV_MOSI_1 PORTC |= (1<<PORTC2) //DRV_MOSI=1

//----------
unsigned int Digit[8]; //массив беззнаковых целочисленных переменных Digit из 8 переменных (8 разрядов драйвера 7seg LED-дисплея)
unsigned char DRV_MOSI[8]; //массив беззнаковых однобайтных символьных переменных DRV_MOSI из 8 переменных (адреса и команды для конфигурирования драйвера)
unsigned char n; //символьная переменная (максимальное количество разрядов led-драйвера n=8)
unsigned char V; //символьная переменная V (значение регистра ацп с результатом оцифровки напряжения)
unsigned char I; //символьная переменная I (значение регистра ацп с результатом оцифровки тока)
unsigned int count; //переменная для задержки обновления данных на индикаторах

unsigned short dt_v; //12-битный результат оцифровки величины [V]
float Av; //Av=dt_v
float Mv; //окончательный результат работы сглаживающего фильтра в канале [V]
float Mv1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [V]
float Ks_v = 0.05; //коэффициент сглаживания в канале [V]

unsigned short dt_i; //12-битный результат оцифровки величины [I]
float Ai; //Ai=dt_i
float Mi; //окончательный результат работы сглаживающего фильтра в канале [I]
float Mi1 = 0; //сброс промежуточного результата работы сглаживающего фильтра в канале [I]
float Ks_i = 0.05; //коэффициент сглаживания в канале [I]

//--- инициализация CS0 ---
void CS0_ini(void) //функция инициализации порта PB0
{
   DDRB |= (1<<PORTB0); //PB0 на вывод
   PORTB |= (1<<PORTB0); //PB0_hi
}

//--- инициализация CS1 ---
void CS1_ini(void) //функция инициализации порта PB2
{
   DDRB |= (1<<PORTB2); //PB2 на вывод
   PORTB |= (1<<PORTB2); //PB2_hi
}

//--- инициализация генератора ---
void PB1_ini(void) //функция инициализации порта PB1
{
   DDRB |= (1<<PORTB1); //PB1 на вывод (oc1a)
   PORTB &= ~(1<<PORTB1); //PB1 сброс
}

//--- инициализация PB2 ---
/*void PB2_ini(void) //функция инициализации порта PB2 (выход запуска генератора +Vpwr = +9...12v)
{
   DDRB |= (1<<PORTB2); //PB2 на вывод (oc1b)
   PORTB &= ~(1<<PORTB2); //PB2 сброс
}*/

//--- инициализация таймера oc1a ---
void oc1a(void) //функция таймера
{
   ASSR=0x00; //сбрасываем полностью регистр assr
   TCCR1A |= ((1<<COM1A1)|(1<<WGM10)); //Fast PWM oc1a, Clear oc1a on Compare Match, clkT2S/1 (no prescalling)
   TCCR1B |= ((1<<WGM12)|(1<<CS10));
   TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 1
   TCNT1L=0x00;
   OCR1AH=0x00;
   OCR1AL=0x55; //Output Compare Register = dec85 - заполнение шим +DC~33%
   TIMSK=0x00; //сброс регистра timsk
}

//--- инициализация таймера oc1b ---
/*void oc1b(void) //функция таймера
{
   ASSR=0x00; //сбрасываем полностью регистр assr
   TCCR1A |= ((1<<COM1B1)|(1<<WGM10)); //Fast PWM oc1b, Clear oc1b on Compare Match, clkT2S/1 (no prescalling)
   TCCR1B |= ((1<<WGM12)|(1<<CS10));
   TCNT1H=0x00; // Timer Value = 0 сброс счётного регистра таймера 2
   TCNT1L=0x00;
   OCR1BH=0x00;
   OCR1BL=0x64; //Output Compare Register = dec100 - заполнение шим +DC~40%
   TIMSK=0x00; //сброс регистра timsk
}*/

//--- инициализация шины SPI_ADC ---
void ADC_SPI_ini(void)
{
   DDRB &= ~(1<<PORTB4); PORTB |= (1<<PORTB4); //MISO, pull-up=ON
   DDRB |= ((1<<PORTB5)|(1<<PORTB2)|(1<<PORTB0)); //выход ADC_CLK, выход ADC_CS1, выход ADC_CS0
   PORTB |= ((1<<PORTB2)|(1<<PORTB0));
   PORTB &= ~(1<<PORTB5); //сброс шины SPI_ADC
   SPCR = ((1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0)); //включим шину SPI, объ§вим ведущим, SCK=16e+06/128=125kHz
}

//--- функция передачи/приёма данных по шине SPI ---
void SPI_SendByte(char byte)
{
   SPDR = byte; //
   while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤
}
unsigned char SPI_ChangeByte(char byte)
{
   SPDR = byte;
   while(!(SPSR & (1<<SPIF))); //подождем пока данные передадутс¤ (обмен¤ютс¤)
   return SPDR;
}

//--- функция опроса внешнего АЦП mcp3201_0 ---
unsigned int Read_3201_0(unsigned char channel)
{
   unsigned int b1,b2;
   ADC_CS0_0; //CS0=0
   b1=SPI_ChangeByte(0); //первый байт
   b2=SPI_ChangeByte(0); //второй байт
   b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
   b1<<=3;
   b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
   ADC_CS0_1; //CS0=1
   return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- функция опроса внешнего АЦП mcp3201_1 ---
unsigned int Read_3201_1(unsigned char channel)
{
   unsigned int b1,b2;
   ADC_CS1_0; //CS1=0
   b1=SPI_ChangeByte(0); //первый байт
   b2=SPI_ChangeByte(0); //второй байт
   b1=(b1<<8)|b2; //собираем два байта в двухбайтовую величину
   b1<<=3;
   b1>>=4; //убираем ненужные биты (3 слева и 1 справа)
   ADC_CS1_1; //CS1=1
   return b1; //возвращаем 12-битный результат ацп-преобразования
}

//--- инициализация шины данных SPI_DRV драйвера max7219 ---
void DRV_SPI_ini(void)
{
   DDRC = 0x07; //биты PС0-PС2 порта PС на вывод
   PORTC &= ~((1<<PORTC2)|(1<<PORTC1)|(1<<PORTC0)); //PС0-PС2 сброс
}

//--- функция побитовой отправки данных в драйвер max7219 ---
void Send_max7219(unsigned char rg, unsigned char dt) //
{
   unsigned char rg_copy; //копия значения переменной rg
   unsigned char i; //переменная для побитной отправки данных в драйвер индикаторов
   
   DRV_MOSI[rg] = dt; //
   rg_copy = rg; //создадим копию значения переменной rg
   
   DRV_CS_0; //отправим «0» на вывод CS микросхемы MAX7219, чтобы начать процесс передачи адреса и данных
   asm("nop"); //пауза в 1 такт
   
   for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта адреса в микросхему MAX7219
   {
      if((rg & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
      {
         DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
      }
      else //если же rg * 0b1000 0000 = 0, ...
      {
         DRV_MOSI_0; //...отправим 0 на вывод Din микросхемы MAX7219
      }
      
      //создадим тактовый импульс на выводе CLK микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
      rg <<= 1; //сдвинем значение переменной rg на 1 бит влево
   } //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта адреса в микросхему MAX7219 будет окончена
   
   for(i=0;i<8;i++) //цикл от 0 до 7 с шагом 1, для побитовой отправки байта данных в микросхему MAX7219
   {
      if((DRV_MOSI[rg_copy] & 0x80)==0x80) //пока rg * 0b1000 0000 > 0, ...
      {
         DRV_MOSI_1; //...отправим 1 на вывод Din микросхемы MAX7219
      }
      else //если же rg * 0b1000 0000 = 0, ...
      {
         DRV_MOSI_0; //отправим 0 на вывод Din микросхемы MAX7219
      }
      
      //создадим тактовый импульс на выводе CLK микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_1; //отправим 1 на вывод Clk микросхемы MAX7219
      asm("nop"); //пауза в 1 такт
      DRV_CLK_0; //отправим 0 на вывод Clk микросхемы MAX7219
      DRV_MOSI[rg_copy] <<= 1; //сдвинем значение переменной DRV_MOSI на 1 бит влево
   }
   //выйдем из цикла когда i станет равной 7, т.е. когда отправка байта данных в микросхему MAX7219 будет окончена
   DRV_CS_1; //отправим «1» на вывод CS микросхемы MAX7219, чтобы завершить процесс передачи адреса и данных
}

//--- инициализация драйвера max7219 ---
void MAX7219_ini(void)
{
   Send_max7219(0x09,0xFF); //(номер регистра, данные) включаем режим BCD code B, для 0-7 разрядов
   Send_max7219(0x0A,0x0A); //DC = 21/32 яркость свечения
   Send_max7219(0x0B,0x07); //число используемых разрядов (0-7 разрядов)
   Send_max7219(0x0C,0x01); //отключаем режим энергосбережения (Shutdown)
   
   for(n=1;n<9;n++)
   {
     Send_max7219(n,0x0F); //гасим все разряды драйвера дисплея V/A до момента вывода информации
   }   
}

//--- функция оцифровки напряжения ---
float ADCV_Conv(unsigned short dt_v)
{
   float dt_v1; //величина измеренного напряжения [V]
   dt_v1=((float)dt_v*(Vref))/Kv; //преобразование 12-битного числа типа u_int в величину измеренного напряжения (float)
   return dt_v1; //возвращаем величину измеренного напряжения в вольтах [V]
}

//--- функция оцифровки тока ---
float ADCI_Conv(unsigned short dt_i)
{
   float dt_i1; //величина измеренного тока [I]
   dt_i1=((float)dt_i*(Vref))/Ki; //преобразование 12-битного числа типа u_int в величину измеренного тока (float)
   return dt_i1; //возвращаем величину измеренного тока в амперах [A]
}

//--- функция вывода значений на индикатор вольтметра 000.0v ---
void ledprint_1(unsigned int number) //number - величина измеренного напряжения Mv
{
   if((float)Mv < 4090) //если Mv <= 409.0, то выводим на дисплей результат...
   {
      Digit[8]=number/1000 ? number/1000 : 0x0F; //тысячи /гасим старший незначащий нуль
      Digit[7]=number/1000 || number%1000/100 ? number%1000/100 : 0x0F; //сотни /гасим младший незначащий нуль
      Digit[6]=number%100/10; //десятки
      Digit[6]=Digit[6]|0x80; //вкл децимальную точку в разряде Digit_6
      Digit[5]=number%10; //единицы
   }
   else //...в противном случае, выводим на дисплей -0L- (overload)
   {
      Digit[8]=0x0A; //<<->>
      Digit[7]=0x00; //<<0>>
      Digit[6]=0x0D; //<<L>>
      Digit[5]=0x0A; //<<->>
   }
   
   for(V=5;V<9;V++) //заполняем разряды вольтметра цифрами разложенного Mv
   {
      DRV_MOSI[V] = Digit[V]; //
      Send_max7219(V,DRV_MOSI[V]); //
   }
}

//--- функция вывода значений на индикатор амперметра ---
void ledprint_2(unsigned int number)
{
   if((float)Mi < 9980) //если Mi < 9.980, то выводим на дисплей результат...
   {
      Digit[4]=number/1000; //тысячи
      Digit[4]=Digit[4]|128; //вкл децимальную точку в разряде Digit_4
      Digit[3]=number%1000/100; //сотни
      Digit[2]=number%100/10; //десятки
      Digit[1]=number%10; //единицы
   }
   else //...в противном случае, выводим на дисплей -0L- (overload)
   {
      Digit[4]=0x0A; //<<->>
      Digit[3]=0x00; //<<0>>
      Digit[2]=0x0D; //<<L>>
      Digit[1]=0x0A; //<<->>
   }
   
   for(I=1;I<5;I++)
   {
      DRV_MOSI[I] = Digit[I];
      Send_max7219(I,DRV_MOSI[I]);
   }
}

//--- основная функция с бесконечным циклом ---
int main(void)
{
   float dt_v=0; //сброс на ноль SPDR
   float dt_i=0; //сброс на ноль SPDR
   CS0_ini(); //инициализация порта PB0
   CS1_ini(); //инициализация порта PB2
   PB1_ini(); //инициализация порта PB1
   //PB2_ini(); //инициализация порта PB2
   oc1a(); //инициализация генератора
   OCR1AH = 0x00; //запись в регистр сравнения ocr1a...
   OCR1AL = 0x55; //...числа dec85
   //oc1b(); //инициализация инвертора +5v >> +9...12v
   //OCR1BH = 0x00; //запись в регистр сравнения ocr1a...
   //OCR1BL = 0x64; //...числа dec100
   ADC_SPI_ini(); //инициализация шины SPI
   DRV_SPI_ini(); //инициализация шины данных драйвера
   MAX7219_ini(); //инициализация MAX7219
   //ADCV_ini(); //инициализация внутреннего АЦП
   //----------
   while(1)
   {
      dt_v = ADCV_Conv(Read_3201_0(0)); //считаем значение SPDR АЦП напряжения
      Av=dt_v; //
      Mv = Ks_v * Av + Mv1 * (1-Ks_v); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
      Mv1=Mv; //      
      dt_i = ADCI_Conv(Read_3201_1(1)); //считаем значение SPDR АЦП тока
      Ai=dt_i; //
      Mi = Ks_i * Ai + Mi1 * (1-Ks_i); //фильтр Кальмана Mn=Ks*An + Mn1*(1-Ks)
      Mi1=Mi; //
      
      count++;  //инкрементируем счётчик ацп-преобразователя
      if(count==750) // выводим на дисплеи результат только каждого n=750 преобразования
      {
          count=0; //сброс счётчика ацп-преобразований
        ledprint_1(Mv); //отправим значение в функцию вывода на индикатор вольтметра
        ledprint_2(Mi); //отправим значение в функцию вывода на индикатор амперметра      
      }            
   }
}









Вложения
copper_24_5_12_12.pdf
(256.34 KiB) Скачиваний: 20

Re: ATmega + 12bit ADC

Пт окт 06, 2023 16:20:43

вау-вау-метер
Вложения
display_board_max7219.pdf
(141.35 KiB) Скачиваний: 20

Re: ATmega + 12bit ADC

Ср ноя 01, 2023 13:56:42

Спойлерновейшее кино про лёд-головку цифровую 100vdc 200adc. для колибровки было задействовано несколько источников тока постояного напряженья. но един член для выстаки верха диапазона энерхетики не хватилло. старую убогую кэтайску узкоглазую с шунтом я выколупал. она на сотку ампериев была. разбежка в покозаниях вольтмера связана с конечной проводимосью верёвок в симинсах и неудачной точкой подключенья внутри. новую голову обернул просто в пакетон от свища и хомутками прищипил. в соседнем дурдоме один пациэнт дюже сильно пережывал за ленейность показомера и в итоге сказал что оно это ему задарром не нада. я првда ему ниччего и не навязывал и посоветовал ему если он в следущий рааз будет пороходить мимо проходить мимо. даальним леском.
Ответить