Спойлер
#include <SPI.h>
#include <EEPROM.h>
#define weight_of_standard 100 // Калибровочный вес
#define power_down() \
\
bitSet(_SFR_IO8(_Write), _clkPin); \
delayMicroseconds(64)
#define power_up() bitClear(_SFR_IO8(_Write), _clkPin) // Макрос для включения питания АЦП
#define GAIN64A 2 // Установка Коэффициента усиления
#define REF_GR_DAC 140.00
#define SCALE_CH 0
#define Scale_coeff 380
volatile bool tare_sig = false;
class DAC {
public:
DAC() {
}
void begin(uint8_t CS, byte Regs[2]) { // Инициализация ЦАП, Pins - пины SPI
_DACMode = Regs[0];
_DACWrite = Regs[1];
CS_PIN = CS;
bitSet(_SFR_IO8(_DACMode), CS_PIN);
bitSet(_SFR_IO8(_DACWrite), CS_PIN);
// Начало транзакции, 20МГц, Старший байт первый, Мод0
}
void write(float value) { // Запись значения в ЦАП
if (value < 0) { // Если значение опускается меньше нуля, записывать ноль
_VAL = 0;
} else if (value > REF_GR_DAC) { // Если значение выше референсного, записывать максимальное
_VAL = 65535;
} else {
_VAL = uint16_t(65535 * value / REF_GR_DAC); // Расчет значения по пропорции
}
bitClear(_SFR_IO8(_DACWrite), CS_PIN); // Запись в чипселект 0 для выбора ЦАП для передачи
SPI.transfer16(_VAL); // передача ЦАП
//delay(15);
bitSet(_SFR_IO8(_DACWrite), CS_PIN); // Запись в чипселект 1 для окончания передачи
}
uint16_t get_value() {
return _VAL;
}
//MSBFIRST в настройке spi = избегаем разделения значений по двум байтам, выдача начинается со старшего байта
private:
uint16_t _VAL;
uint8_t CS_PIN;
byte _DACMode, _DACWrite;
};
class SCALE {
public:
/*Порядок записи в массив: DR, DW, DM, CW, CM, DP, CP*/
SCALE(int i = 1) {
}
/*ИНИЦИАЛИЗАЦИЯ ДАТЧИКА*/
void begin(byte Regs[3], byte Pins[2]) { // Инициализация АЦП
/*Адресса регистров HX PIND, PORTD, DDRD PORTB DDRB*/
_Read = Regs[0]; // Запись значений регистров и пинов
_Write = Regs[1];
_Mode = Regs[2];
_dataPin = Pins[0];
_clkPin = Pins[1];
bitSet(_SFR_IO8(_Mode), _clkPin); // Настройка регистров
bitClear(_SFR_IO8(_Mode), _dataPin);
bitClear(_SFR_IO8(_Write), _clkPin);
bitClear(_SFR_IO8(_Write), _dataPin);
power_down(); // Выключение и включение АЦП
power_up();
flag_filt = 0;
_chan = GAIN64A;
_weight = 0;
_value = 0;
_offset = 0;
_scale = 1;
}
/*УСТАНОВКА МАСШТАБА*/
void set_scale(float scale) {
_scale = 1.0 / scale;
}
/*КАЛИБРОВКА*/
void calibrate_scale(uint8_t weight) {
long read_av = 0;
for (uint8_t i = 0; i < 15; i++) { // Калибровка на 15 значениях среднее арифметическое
read_av += read();
}
read_av = read_av / 15;
_scale = (1.0 * weight) / (read_av - _offset); // получаем масштаб
}
/*ВЕРНУТЬ МАСШТАБ*/
float get_scale() {
return 1 / _scale;
}
/*ТАРИРОВАНИЕ*/
void tare() {
_offset = 0;
for (uint8_t i = 0; i < 3; i++) { // среднее арифметическое по трем замерам
_offset += read();
}
_offset = _offset / 3;
}
/*ЧТЕНИЕ СЫРОГО ЗНАЧЕНИЯ*/
long read() {
while (bitRead(_SFR_IO8(_Read), _dataPin) == HIGH) yield();
_weight = 0;
for (uint8_t i = 0; i < 24; i++) { // посылка сигналов тактирования
bitSet(_SFR_IO8(_Write), _clkPin); //digitalWrite(_clock, HIGH);
delayMicroseconds(1);
_weight <<= 1;
if (bitRead(_SFR_IO8(_Read), _dataPin)) _weight |= 1;
bitClear(_SFR_IO8(_Write), _clkPin); //digitalWrite(_clock, LOW);
delayMicroseconds(1);
}
for (uint8_t i = 0; (i < _chan + 1); i++) {
bitSet(_SFR_IO8(_Write), _clkPin); //digitalWrite(_clock, 1);
delayMicroseconds(1);
bitClear(_SFR_IO8(_Write), _clkPin); //digitalWrite(_clock, 0);
delayMicroseconds(1);
}
if (_weight & 0x800000) _weight |= 0xFF000000; // отрицательные
return _weight;
}
/*ПЕРЕВОД СЫРОГО ЗНАЧЕНИЯ В ГРАММЫ*/
float value() {
read();
_value = (_weight - _offset) * _scale;
return _value;
}
/*ВЕРНУТЬ ЗНАЧЕНИЕ В ГРАММАХ*/
float get_value() {
return _value;
}
/*ФИЛЬТРАЦИЯ ЗНАЧЕНИЯ В ГРАММАХ*/
float filter() {
noInterrupts();
float newVal = value();
byte k = 5;
if (!flag_filt) filt = newVal;
flag_filt = true;
if (abs(newVal - filt) <= 0.5) k = 150; // Выбор коэффициента для бегущего среднего в зависимости от величины изменения
else if (abs(newVal - filt) >= 0.51 && abs(newVal - filt) < 2.7) k = 4;
else if (abs(newVal - filt) >= 2.7 && abs(newVal - filt) < 3.5) k = 2;
else if (abs(newVal - filt) >= 3.5 && abs(newVal - filt) < 5) k = 6;
else if (abs(newVal - filt) >= 5 && abs(newVal - filt) < 7) k = 10;
else if (abs(newVal - filt) >= 7) k = 20;
filt += (newVal - filt) / k; // фильтрация по бегущему среднему
return filt;
interrupts();
}
float filt;
private:
byte _Read, _Write, _Mode;
byte _dataPin, _clkPin, _chan;
volatile long _weight, _offset;
float _scale, _value;
bool flag_filt;
};
SCALE scale[4]; // создание объектов АЦП
DAC dac[4]; // Создание объектов ЦАП
// Значения веса с датчиков
float values[4] = { 0, 0, 0, 0 };
/*Адресса регистров HX PIND, PORTD, DDRD */
byte ADC_D_Regs[3] = { 0x09, 0x0B, 0x0A };
// DDRC PORTC
byte DAC_C_Regs[2] = { 0x07, 0x08 };
// Пины CS для DAC CS0 CS1 CS2 CS3
byte SPI_CS[4] = { 0, 1, 2, 3 };
// Коэффициенты для АЦП 1-4 каналы
float scale_coeffs[4];
//float scale_coeffs_hard[4] = {-368.68, 917, -430.39, 528};
// Пины данных и тактирования для АЦП
byte ADC_D_Pins[4][2] = {
{ 1, 0 },
{ 2, 3 },
{ 4, 5 },
{ 6, 7 },
};
bool flag = false; // флаг для калибровки
// Функции работы с каналами: чтение и запись канала SCALE_CH, всех четырех, возврат масштаба первого канала, возврат канала 2-4
void four_ch_test(), calibration(int channel);
uint8_t oldpinc = 0xFF;
volatile bool interrupt_state = 0;
ISR(PCINT1_vect) { // Обработчик прерывания на пине А4
uint8_t changedbits = PINC ^ oldpinc;
oldpinc = PINC;
if (interrupt_state == 0) {
if (changedbits & (1 << PC4)) { // Изменился A4, тарирование
for (int i = 0; i < 4; i++) {
scale[i].tare(); // тарирование
scale[i].filt = 0;
}
// digitalWrite(13, HIGH);
// delay(1500);
// digitalWrite(13, LOW);
}
if (changedbits & (1 << PC5)) { // Изменился A5, калибровка
for (int i = 0; i < 4; i++) {
calibration(i); // калибровка
}
}
interrupt_state = 1;// Установка флага тарирования
}
else if (interrupt_state == 1){
tare_sig = true;
interrupt_state = 0;
}
}
void setup() {
// Serial.begin(9600);
SPI.begin(); // Запуск интерфейса SPI
SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));
delay(2000);
DDRC &= ~(1 << 4) & ~(1 << 5);
PORTC |= (1 << 4) | (1 << 5);
SREG |= 1 << SREG_I;
PCICR |= 1 << 1;
PCMSK1 |= (1 << 4) | (1 << 5); // НАСТРОЙКА ПРЕРЫВАНИЯ НА ПИНАХ А4 и А5
// Инициализация 4 ДАТЧИКОВ
for (int i = 0; i < 4; i++) {
scale[i].begin(ADC_D_Regs, ADC_D_Pins[i]); // инициализация АЦП
scale[i].tare(); // тарирование
EEPROM.get(i*4, scale_coeffs[i]);
scale[i].set_scale(scale_coeffs[i]);
dac[i].begin(SPI_CS[i], DAC_C_Regs); // инициализация ЦАП
}
// calibration(3);
// for (int i = 0; i<4; i++) {
// calibration(i);
// }
// Serial.begin(9600);
// for (int i = 0; i<4; i++) {
// EEPROM.get(i*4, scale_coeffs[i]);
// Serial.print("/n");
// Serial.print(scale_coeffs[i]);
// }
}
/* Список действий на производстве
0. Установка кейсов
1. Припаивание датчиков
2. Подключение датчиков к ардуино
3. Произведение калибровки: Запуск функции calibration() для каждого датчика поочередно. Калибровка записывает данные в EEPROM
поэтому можно спокойно перезапускать контроллер
4. Установить программу с основной функцией four_ch_test()
5. Подключить ардуино к ПЛК
*/
void loop() {
tare_sig = false;
while (!tare_sig) { // Выполнение при активном флаге
tare_sig = false;
four_ch_test();
}
}
void four_ch_test() {
tare_sig = false;
for (byte i = 0; (i < 4) && !tare_sig; i++) { // Выполнение при активном флаге
values[i] = scale[i].filter(); //запись фильтрованного значения
}
for (byte i = 0; (i < 4) && !tare_sig; i++) {
dac[i].write(values[i]); // запись в ЦАП
}
}
void calibration(int ch) {
digitalWrite(13, LOW);
delay(2000);
digitalWrite(13, HIGH);
delay(5000);
scale[ch].calibrate_scale(weight_of_standard);
EEPROM.put(ch*4, scale[ch].get_scale());
digitalWrite(13, LOW);
delay(3000);
}