Теряются значения переменных основной программы при выходе из прерывания.

Technolog
Offline
Зарегистрирован: 19.11.2014

Arduino Mega 2560.
Суть проблемы:
Master с интервалом в 1 минуту запрашивает поочередно температуры измеренные 4-мя датчиками.
По приходу первого байта запроса срабатывает обработчик прерывания ISR(USART0_RX_vect).
При некорректном принятии запрос игнорируется, при корректном -
в обработчике формируется массив символов запроса, определяется условный адрес запрашиваемого датчика, формируется ответ Masterу, отправляется, и здесь выход из прерывания. Идет 4 запроса-ответа подряд, естественно еще минимум три раза вызывается прерывание.
Первый ответ содержит действительную температуру датчика, но последующие три ответа
(2, 3 и 4 датчики) содержат ноль по температуре.
Причина: обнуляются переменные температур 2, 3 и 4 при первом выходе из прерывания по USART.
Переменные температур обьявлены как volatile.
Где ошибка? Способ решения?

#include <avr/interrupt.h> //библиотека прерываний
#include <OneWire.h>
#include <DallasTemperature.h> 
 ...
   //переменные задействованные в void loop() и в обработчике 
   //прерывания ISR(USART0_RX_vect) определены как  volatile float
volatile float val_T1;  температура 1-го датчика
volatile float val_T2;  температура 1-го датчика
volatile float val_T3;  температура 1-го датчика
volatile float val_T4;  температура 1-го датчика
volatile unsigned char Symb;
volatile unsigned char s[16];
 ... 
  // функция инициализации прерывания по приходу байта ч/з USART
void USART_Init(int baudrate )
 {
   UBRR0H = baudrate>>8;
   UBRR0L = baudrate;
   UCSR0B = (1<<RXCIE0)|(1<<TXCIE0)|(1<<RXEN0)|(1<<TXEN0);
   UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
   sei();
 }
 ... 
void setup() 
{ 
 USART_Init(103); 
 ...
}
 ...
void USART_Transmit( unsigned char data )//Функция отправки данных
{...}
unsigned char USART_Receive( void )//Функция приема данных
{...}
 ... 
void loop()
{  
 * * * * *  
 ...
sensors_1.requestTemperatures();
val_K1T1 = sensors_1.getTempC(add_K1T1);
val_K1T2 = sensors_1.getTempC(add_K1T2);
val_K1T3 = sensors_1.getTempC(add_K1T3);

sensors_2.requestTemperatures();
val_K1T4 = sensors_2.getTempC(add_K1T4);
}
 ... 
//Обрабатываем прерывание по поступлению байта
ISR(USART0_RX_vect)
{  	
  // UDR0; //прининятый байт  
  s[14] = 0;
  if (UDR0 == 35) 
{
  for (byte ii=2; ii<=14; ii++) {s[ii] = USART_Receive();}
  if (s[14] == 13) 
{
  ArrReq[0] = s[2];
  ArrReq[1] = s[3];  
 ...
   // определяется условный адрес опрашиваемого датчика
    Sens_addres = 128*ArrOut[1] + 64*ArrOut[2] + ... + ArrOut[8];    
 ...
   // переменной Temp присваивается значение температуры измеренное датчиком 
if (Sens_addres == 120) {Temp = val_T1;}  1-й датчик
if (Sens_addres == 121) {Temp = val_T2;}  2-й датчик
if (Sens_addres == 122) {Temp = val_T3;}  3-й датчик
if (Sens_addres == 123) {Temp = val_T4;}  4-й датчик
 ...
  // формируется строка символов для ответа
 ...
// отправка строки символов, (ответ на запрос Master'a): 
    delay(2); // требуется по протоколу обмена с Master'ом
    for (byte i=1; i<=27; i++) {USART_Transmit(ArrPac[i]);}
}
}
}
brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Тут у вас не весь листинг. То что вы описали возможно из-за нехватки памяти. Компилятор не контролирует пересечение адресов памяти с адресами стека или уход указателя за пределы массива и пр. и др. Кроме того есть такие "интересные" директивы как ststic и public. Директивы можно просто поробывать. А вот первые две возможные причины вычислить трудно. Но скорее всего проблема в тек кусках кода которые вы скрыли. 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

В чем проблема, не знаю, куча текста скрыта. Но это не настолько важно, сколько вызов в обработчике прерывания по получению данных вызывается приём остальных данных в цикле. А в конце ещё и передача данных. Обработчик прерываний должен быть коротким, не вызывать функций задержки и т.п. А обработку того, что принял обработчик прерываний нужно делать в loop.

 

Technolog
Offline
Зарегистрирован: 19.11.2014

brokly, kisoft спасибо за разьяснение и направление.

Пробую static. Бегло не прошло: компилятор не ругается о невидимости переменной в обработчике прерывания но и в ответе пока ноли по температуре. Изучу видимости, продолжу.

Обрабатывать полученные байты в loop не могу т.к. там процессы опросов датчиков медленные и непредсказуемые по времени, а ответ на запрос нужно дать в течении 2-5 мс после прихода последнего 14-го байта запроса. Как выйти из обработчика в другое место той же loop (где  и описать формирование и отправку ответа) не знаю. Вопрос: можно ли выйти из обработчика прервания по приему байта USART  в другое место программы, не туда где прерывание было вызвано? (интернет по этому поводу читал, но готовых решений ни одного не видел, только теория с подменой адреса возврата в стеке. высшая математика!)

Задумался,  можно ли организовать "свое" хранение данных в дальних нетрогаемых программой регистрах оперативной памяти (не флеш!), по надобности обновлять там значения температур вычисленные в loop и оттуда же считывать обработчиком прерывания. Будет однозначно. Уйти от вижу-невижу-обноляю  в  работе с прерываниями, от ограничений типа короткого кода. Или это лишнее, есть "правильные" пути?

Весь код:

#include <avr/interrupt.h> //библиотека прерываний
#include <OneWire.h>
#include <DallasTemperature.h> 
/*--------------------------------------------------------------------------*/
// Провод данных подключен к порту 22 и 23 на Arduino
#define OW_Bus1 22
#define OW_Bus2 23 
/*--------------------------------------------------------------------------*/
// Настройка-Wire экземпляр общаться с любым Wire для устройств (не только температуры ИС Максим / Даллас)
OneWire oneWire_1 (OW_Bus1);
OneWire oneWire_2 (OW_Bus2);
/*--------------------------------------------------------------------------*/
DallasTemperature sensors_1(&oneWire_1);
DallasTemperature sensors_2(&oneWire_2); 
/*--------------------------------------------------------------------------*/
DeviceAddress add_K1T1 = {0x28, 0xEB, 0x92, 0xC7, 0x02, 0x00, 0x00, 0x0B};
DeviceAddress add_K1T2 = {0x28, 0x59, 0x5B, 0xC7, 0x02, 0x00, 0x00, 0xB3};
DeviceAddress add_K1T3 = {0x28, 0xC0, 0x8A, 0xC7, 0x02, 0x00, 0x00, 0x07};
DeviceAddress add_K1T4 = {0x28, 0xE9, 0x52, 0xC7, 0x02, 0x00, 0x00, 0x47};
/*--------------------------------------------------------------------------*/
volatile float val_K1T1;
volatile float val_K1T2;
volatile float val_K1T3;
volatile float val_K1T4;
/*---------------------------------------------*/
  unsigned long TimeS;
  unsigned long TimeIn;
  unsigned long TimeOut;
  char ArrReq[16];  // массив символов пришедшего пакета
  char ArrPac[28];  // массив символов отправляемого пакета
  byte ArrOut[96];  // двоичный массив для формирования выводной строки
  byte ArrCRC[96];  // двоичный массив для вычисления CRC
  byte DelCRC[] = {0,1,1,0,0,0,1,1,1,1,0,1,0,1,0,1,1,1};  // массив делителя CRC ОВЕН
  byte ResCRC[16];  // массив остатка CRC
  byte BitReq;  // значение бита запроса: 1 - запрос параметра программой, 0 - запись управляющего параметра в прибор
  byte LenAdd;  // длина блока данных в байтах
  byte IndPer = 0;  // индикатор передачи ответа на запрос: 1 - передавать, 0 - не передавать
   byte FloTwo[32];  // - двоичный массив float числа
   byte FloPor;  // - порядок float числа, целое число (от 127)
   byte PorBit;
/*--------------------------------------------------------------------------*/   
byte it;
byte present = 0;
byte type_s;
byte data[12];
byte addr[8];
float celsius;
float Temp;
byte  MVA_addres;
byte NumReq = 1;
/*--------------------------------------------------------------------------*/
//volatile unsigned char ii;
//volatile unsigned char Symb;
volatile unsigned char s[16];//Массив для приема байтов
/*--------------------------------------------------------------------------*/

void USART_Init(int baudrate ) //Функция инициализации USART
 {
   /* Установите скорость передачи данных */
   UBRR0H = baudrate>>8;
   UBRR0L = baudrate;
   //Разрешение на прием и на передачу через USART, прерывания по поступлению и по опустошению 
   UCSR0B = (1<<RXCIE0)|(1<<TXCIE0)|(1<<RXEN0)|(1<<TXEN0);
   UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); //размер слова 8 разрядов
   sei();   
 } 
/*--------------------------------------------------------------------------*/
 
void setup() 
{ 
 USART_Init(103);// число 103 соответствует baudrate 9600 при 16MHz. смотреть datasheet нa ATMega 2560  
  
//  Serial.begin(9600);

  // Запускаем библиотеку  
  sensors_1.begin();  
  sensors_2.begin();
  
  sensors_1.setResolution(add_K1T1, 12);
  sensors_1.setResolution(add_K1T2, 12);
  sensors_1.setResolution(add_K1T3, 12);
  sensors_2.setResolution(add_K1T4, 12);
}
/*--------------------------------------------------------------------------*/

void USART_Transmit( unsigned char data )//Функция отправки данных
{
  /* Подождите пустой буфер передачи */
  while ( !( UCSR0A & (1<<UDRE0)) );
  /* Положите данные в буфер, отправляет данные */
  UDR0 = data;
}
/*--------------------------------------------------------------------------*/

unsigned char USART_Receive( void )//Функция приема данных
{
  /* Подождите данные, которые будут получены */
  while ( !(UCSR0A & (1<<RXC0)) );
  /* Получить и вернуть полученные данные из буфера */
  return UDR0;
}

/****************************************************************************/ 

//Обрабатываем прерывание по поступлению байта
ISR(USART0_RX_vect)
{  	
  // UDR0; //прининятый байт  
  s[14] = 0;
  if (UDR0 == 35) 
{
  for (byte ii=2; ii<=14; ii++) {s[ii] = USART_Receive();}
  if (s[14] == 13) 
{
  ArrReq[0] = s[2];
  ArrReq[1] = s[3];

/****************************************************************************/
// формируем двоичный массив отправляемого пакета ArrOut[96]: 
  // 1...8 - адрес, 1 байт
  // 9...11 - 000 - нолевые биты адреса (используем 8 битную адресацию)
  // 12 - бит запроса (1 - запрос параметра программой, 0 - запись управляющего параметра в прибор)
  // 13...16 - биты длины блока данных в байтах
  // 17...32 - биты Hash слова rEAD с делителем CRC ОВЕН, 2 байта
  // 33...64 - биты float числа передаваемого параметра, 4 байта
  // 65...80 - биты времени опроса, 2 байта
  // 81...96 - биты остатка CRC, 2 байта
/*--------------------------------------------------------------------------*/  
// присваивание 1...8 битам значений (адрес)
    if (ArrReq[0] == 'G') {ArrOut[1] = 0; ArrOut[2] = 0; ArrOut[3] = 0; ArrOut[4] = 0;}
    if (ArrReq[0] == 'H') {ArrOut[1] = 0; ArrOut[2] = 0; ArrOut[3] = 0; ArrOut[4] = 1;}
    if (ArrReq[0] == 'I') {ArrOut[1] = 0; ArrOut[2] = 0; ArrOut[3] = 1; ArrOut[4] = 0;}
    if (ArrReq[0] == 'J') {ArrOut[1] = 0; ArrOut[2] = 0; ArrOut[3] = 1; ArrOut[4] = 1;}
    if (ArrReq[0] == 'K') {ArrOut[1] = 0; ArrOut[2] = 1; ArrOut[3] = 0; ArrOut[4] = 0;}
    if (ArrReq[0] == 'L') {ArrOut[1] = 0; ArrOut[2] = 1; ArrOut[3] = 0; ArrOut[4] = 1;}
    if (ArrReq[0] == 'M') {ArrOut[1] = 0; ArrOut[2] = 1; ArrOut[3] = 1; ArrOut[4] = 0;}
    if (ArrReq[0] == 'N') {ArrOut[1] = 0; ArrOut[2] = 1; ArrOut[3] = 1; ArrOut[4] = 1;}
    if (ArrReq[0] == 'O') {ArrOut[1] = 1; ArrOut[2] = 0; ArrOut[3] = 0; ArrOut[4] = 0;}
    if (ArrReq[0] == 'P') {ArrOut[1] = 1; ArrOut[2] = 0; ArrOut[3] = 0; ArrOut[4] = 1;}
    if (ArrReq[0] == 'Q') {ArrOut[1] = 1; ArrOut[2] = 0; ArrOut[3] = 1; ArrOut[4] = 0;}
    if (ArrReq[0] == 'R') {ArrOut[1] = 1; ArrOut[2] = 0; ArrOut[3] = 1; ArrOut[4] = 1;}
    if (ArrReq[0] == 'S') {ArrOut[1] = 1; ArrOut[2] = 1; ArrOut[3] = 0; ArrOut[4] = 0;}
    if (ArrReq[0] == 'T') {ArrOut[1] = 1; ArrOut[2] = 1; ArrOut[3] = 0; ArrOut[4] = 1;}
    if (ArrReq[0] == 'U') {ArrOut[1] = 1; ArrOut[2] = 1; ArrOut[3] = 1; ArrOut[4] = 0;}
    if (ArrReq[0] == 'V') {ArrOut[1] = 1; ArrOut[2] = 1; ArrOut[3] = 1; ArrOut[4] = 1;}
  
    if (ArrReq[1] == 'G') {ArrOut[5] = 0; ArrOut[6] = 0; ArrOut[7] = 0; ArrOut[8] = 0;} 
    if (ArrReq[1] == 'H') {ArrOut[5] = 0; ArrOut[6] = 0; ArrOut[7] = 0; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'I') {ArrOut[5] = 0; ArrOut[6] = 0; ArrOut[7] = 1; ArrOut[8] = 0;}  
    if (ArrReq[1] == 'J') {ArrOut[5] = 0; ArrOut[6] = 0; ArrOut[7] = 1; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'K') {ArrOut[5] = 0; ArrOut[6] = 1; ArrOut[7] = 0; ArrOut[8] = 0;} 
    if (ArrReq[1] == 'L') {ArrOut[5] = 0; ArrOut[6] = 1; ArrOut[7] = 0; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'M') {ArrOut[5] = 0; ArrOut[6] = 1; ArrOut[7] = 1; ArrOut[8] = 0;}  
    if (ArrReq[1] == 'N') {ArrOut[5] = 0; ArrOut[6] = 1; ArrOut[7] = 1; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'O') {ArrOut[5] = 1; ArrOut[6] = 0; ArrOut[7] = 0; ArrOut[8] = 0;} 
    if (ArrReq[1] == 'P') {ArrOut[5] = 1; ArrOut[6] = 0; ArrOut[7] = 0; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'Q') {ArrOut[5] = 1; ArrOut[6] = 0; ArrOut[7] = 1; ArrOut[8] = 0;}  
    if (ArrReq[1] == 'R') {ArrOut[5] = 1; ArrOut[6] = 0; ArrOut[7] = 1; ArrOut[8] = 1;} 
    if (ArrReq[1] == 'S') {ArrOut[5] = 1; ArrOut[6] = 1; ArrOut[7] = 0; ArrOut[8] = 0;}  
    if (ArrReq[1] == 'T') {ArrOut[5] = 1; ArrOut[6] = 1; ArrOut[7] = 0; ArrOut[8] = 1;}  
    if (ArrReq[1] == 'U') {ArrOut[5] = 1; ArrOut[6] = 1; ArrOut[7] = 1; ArrOut[8] = 0;} 
    if (ArrReq[1] == 'V') {ArrOut[5] = 1; ArrOut[6] = 1; ArrOut[7] = 1; ArrOut[8] = 1;}
    
    MVA_addres = 128 * ArrOut[1] + 64 * ArrOut[2] + 32 * ArrOut[3] + 16 * ArrOut[4] + 8 * ArrOut[5] + 4 * ArrOut[6] + 2 * ArrOut[7] + ArrOut[8] ;    
/*--------------------------------------------------------------------------*/

if (MVA_addres == 120) {Temp = val_K1T1;}
if (MVA_addres == 121) {Temp = val_K1T2;}
if (MVA_addres == 122) {Temp = val_K1T3;}
if (MVA_addres == 123) {Temp = val_K1T4;}
/*--------------------------------------------------------------------------*/

// преобразование числа в формат float, результат: - двоичный массив FloTwo[32]    
   if (Temp == 0) {for (byte i=1; i<=32; i++) {FloTwo[i]=0;}}
   else 
{ 
     
   if (Temp > 0) {FloTwo[1] = 0;} 
   if (Temp < 0) {FloTwo[1] = 1; Temp = - Temp;} else {FloTwo[1] = 0;} // - определяем знак числа
   
   FloPor = 127;
   for (byte i=1; Temp < 1 || Temp >= 2; i++) { // - нормализуем число Temp до вида 1...2 и определяем порядок числа
        if (Temp >= 2) {Temp = Temp / 2; FloPor++;}
        if (Temp < 1) {Temp = Temp * 2; FloPor--;}}
        
   for (byte i=9; i>=2; i--) {  // - записываем порядок числа
        PorBit = FloPor % 2;  // - остаток од деления порядка числа на 2, если =1 то пишем 1, если =0 то пишем 0 (запись идет справа налево потому что число больше единицы и переводим в двоичную систему деля на 2)
        FloPor = floor(FloPor / 2);
        if (PorBit == 1) {FloTwo[i] = 1;} else {FloTwo[i] = 0;}
                             }         
// - записываем число
   for (byte i=10; i<=32; i++) {FloTwo[i] = 0;}
   Temp = Temp - 1;  // - отнимаем целую значащую часть числа которая в приведенном виде всегда равна 1 (таков стандарт отображения float числа согласно стандарту IEEE 754)
   for (byte i=10; i<=32; i++) {  // - умножая дробную часть проверяем её на превышение 1, и если превысило то записываем 1, нет записываем 0.
        Temp = Temp * 2;
        if (Temp >= 1) {FloTwo[i] = 1; Temp = Temp - 1;} 
        else {FloTwo[i] = 0;}  }
}
/*--------------------------------------------------------------------------*/
  
// присваивание 9...11 - 000 - нолевые биты адреса (используем 8 битную адресацию)
    ArrOut[9] = 0; ArrOut[10] = 0; ArrOut[11] = 0;
    
// присваивание 12 - бит запроса (1 - запрос параметра программой, 0 - запись управляющего параметра в прибор)
    ArrOut[12] = 0;
    
// присваивание 13...16 - биты длины блока данных в байтах 
    ArrOut[13] = 0; ArrOut[14] = 1; ArrOut[15] = 1; ArrOut[16] = 0;
    
// присваивание 17...32 - биты Hash слова rEAD с делителем CRC ОВЕН, 2 байта: 0х87 0х84 
    ArrOut[17] = 1; ArrOut[18] = 0; ArrOut[19] = 0; ArrOut[20] = 0;   
    ArrOut[21] = 0; ArrOut[22] = 1; ArrOut[23] = 1; ArrOut[24] = 1;  
    ArrOut[25] = 1; ArrOut[26] = 0; ArrOut[27] = 0; ArrOut[28] = 0;   
    ArrOut[29] = 0; ArrOut[30] = 1; ArrOut[31] = 0; ArrOut[32] = 0;   
    
// присваивание 33...64 - биты float числа передаваемого параметра, 4 байта
   for (byte i=1; i<=32; i++) {ArrOut[i+32] = FloTwo[i];}
    
// присваивание 65...80 - биты времени опроса, 2 байта: пока любое время из реальных данных MO SL
    ArrOut[65] = 0; ArrOut[66] = 1; ArrOut[67] = 1; ArrOut[68] = 0;   
    ArrOut[69] = 1; ArrOut[70] = 0; ArrOut[71] = 0; ArrOut[72] = 0;  
    ArrOut[73] = 1; ArrOut[74] = 1; ArrOut[75] = 0; ArrOut[76] = 0;   
    ArrOut[77] = 0; ArrOut[78] = 1; ArrOut[79] = 0; ArrOut[80] = 1;    
    
// присваивание 81...96 - биты остатка CRC (2 байта) нолей для вычисления остатка CRC   
    ArrOut[81] = 0; ArrOut[82] = 0; ArrOut[83] = 0; ArrOut[84] = 0;   
    ArrOut[85] = 0; ArrOut[86] = 0; ArrOut[87] = 0; ArrOut[88] = 0;  
    ArrOut[89] = 0; ArrOut[90] = 0; ArrOut[91] = 0; ArrOut[92] = 0;   
    ArrOut[93] = 0; ArrOut[94] = 0; ArrOut[95] = 0; ArrOut[96] = 0;   
    
/*--------------------------------------------------------------------------*/ 
// нахождение контрольной суммы CRC:  
for (byte i=1; i<=96; i++) {ArrCRC[i] = ArrOut[i];} // заполняем массив ArrCRC

// собственно вся операция деления для нахождения остатка:
for (byte i=1; i<=80; i++) { // i - счётчик позиции первого обрабатываемого бита массива ArrCRC
     if (ArrCRC[i] == 1) { // совмещение первого символа делителя против первой единицы в делимом
     
  //  for (byte m=1; m<=96; m++) {Serial.print(ArrCRC[m]);} Serial.println("");    
  //  for (byte v=1; v<i; v++) {Serial.print(" ");}
  //  for (byte w=1; w<=17; w++) {Serial.print(DelCRC[w]);} Serial.println("");
    
        {for (byte n=1; n<=17; n++)
             {if (ArrCRC[i+n-1] == DelCRC[n]) {ArrCRC[i+n-1] = 0;}
              else {ArrCRC[i+n-1] = 1;} // побитное вычитание делителя CRC             
             }              
        } 
                         }  } // остаток в ArrCRC[81]...ArrCRC[96]
        
// запись CRC остатка в выходной массив
   for (byte i=81; i<=96; i++) {ArrOut[i] = ArrCRC[i];}    
/*--------------------------------------------------------------------------*/ 

// формирование символьного массива для вывода ответа:
ArrPac[1]=35; ArrPac[26]=13;
for (byte i=0; i<=23; i++) {byte SumTet = ArrOut[i*4+1]*8 + ArrOut[i*4+2]*4 + ArrOut[i*4+3]*2 + ArrOut[i*4+4];
                            if (SumTet == 0) {ArrPac[i+2]='G';}
                            if (SumTet == 1) {ArrPac[i+2]='H';}
                            if (SumTet == 2) {ArrPac[i+2]='I';}
                            if (SumTet == 3) {ArrPac[i+2]='J';}
                            if (SumTet == 4) {ArrPac[i+2]='K';}
                            if (SumTet == 5) {ArrPac[i+2]='L';}
                            if (SumTet == 6) {ArrPac[i+2]='M';}
                            if (SumTet == 7) {ArrPac[i+2]='N';}
                            if (SumTet == 8) {ArrPac[i+2]='O';}
                            if (SumTet == 9) {ArrPac[i+2]='P';}
                            if (SumTet == 10) {ArrPac[i+2]='Q';}
                            if (SumTet == 11) {ArrPac[i+2]='R';}
                            if (SumTet == 12) {ArrPac[i+2]='S';}
                            if (SumTet == 13) {ArrPac[i+2]='T';}
                            if (SumTet == 14) {ArrPac[i+2]='U';}
                            if (SumTet == 15) {ArrPac[i+2]='V';}
                           }
/*--------------------------------------------------------------------------*/

// отправка строки символов, (ответ на запрос): 
    delay(2);
    for (byte i=1; i<=27; i++) {USART_Transmit(ArrPac[i]);}
}
}
/*--------------------------------------------------------------------------*/

void loop()
{
  sensors_1.requestTemperatures();
  val_K1T1 = sensors_1.getTempC(add_K1T1);
  val_K1T2 = sensors_1.getTempC(add_K1T2);
  val_K1T3 = sensors_1.getTempC(add_K1T3);

  sensors_2.requestTemperatures();
  val_K1T4 = sensors_2.getTempC(add_K1T4);
}

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Если Статик для глобальной переменной, то здесь бесполезно.

С loop понятно, запросы с 1-Wire это должно быть море задержек и скорей всего прерывания запрещаются, а если нет, то труба 1-Wire, поплывут времянки.

Выход из прерывания не в точку вызова - проще застрелиться, ни разу не выгодно, потому что не факт, что вызов был из loop. Можно, но костыли и головняк. 

Если на приём нужно 14 байт, то правильней организовать счётчик принятых байтов и на 14-ом реагировать, будет лучше точно. В любом случае как сейчас костыльно, лотерея, как луна по вернётся, скорее работать не будет.

Вывод ответа можно организовать также по прерываниями, т.е. сформировать буфер вывода и пнуть вывод.

Вообще, скрестить 1-Wire программный с асинхронными прерываниями, думаю не реально. Если хотя бы 1-Wire был аппаратным, типа DS2480b, тогда было бы чуть проще.

В целом, сейчас задачу оценить сложно из-за отсутствия ТЗ и ограниченности атмеги (отчасти).

А ещё, отлаживать всё и сразу это иметь кучу проблем. Но в любом случае, даже если получится по частям, то при соединении работать не будет.

Много букв, но такова се ля ви.

 

Technolog
Offline
Зарегистрирован: 19.11.2014

Изучаю работу оперативной памяти и возможность ручной работы с ней. Получится обойти потерю переменных - отпишусь.

Вообще передать все температуры без потерь удается если не выходить из прерывания, а передавать все 4 температуры циклом: вначале обработки стоит метка, а в конце обработки goto на метку. Но тогда не могу выйти из прерывания, бесконечный цикл. Пробовал аппаратно после пропадания активности на проводах Rx Tx в перезагруз по выводу RESET, но там нужно не просто сбросить уровень в LOW но и отпустить иначе не загрузится. Хоть реле ставь, оно с пружиной. Но это уже другая история. Если поставить счетчик отработанных циклов и условие: после четвертого цикла на выход из прерывания, вот тут температуры 2, 3, 4 и теряются!

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

 

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Ок, Вы по какой то причине считаете, что у Вас что то теряется. И не понимаете о чем я говорю. Тогда не буду тратить наше время. Удачи.

Думаю, что если это было бы возможно, то Вы бы удивились, что прерывания у Вас вызываются не 2 не 3 и не 4 раза. И не в те моменты, что Вы предполагаете.

Technolog
Offline
Зарегистрирован: 19.11.2014

Решено.

В одном прерывании обрабатываются все 4 запроса и даются ответы Masterу. Прибор работает.
Факт потери значений переменными проверен но механизм не исследован.

Тема закрыта.

Благодарности brokly и kisoft.

 

axill
Offline
Зарегистрирован: 05.09.2011

В догонку

стоит избегать применения таких типов как fliat и double. Они генерят код интенсивно использующий ram, flash и вычислительные мощности МК. Лучше применять целые. Если требуются цифры после запятой (например десятая доля градуса) лучше паковать в целое умножив на 10. Т.е. Например 26.4 градуса хранить как 264. При всех манипуляциях это учитывать, это позволяет полностью избавится от типов float и double - все операции с ними делаются програмно, у МК нет никаких средств для работы с такими типами. Отсюда неразумное разбрасывание ресурсами

nik182
Offline
Зарегистрирован: 04.05.2015

Совсем короткая программа. Крутится цикл опрашивается датчик и выводятся на LCD текущее и максимальное значение. Время от времени нужно нажать на кнопку, что бы сбросить максимальное значение. Попытался повесить кнопку на прерывание.  В перывании единственная команда - button = true. Оказалось, что при выходе из прерывания случайным образом  button превращается в ноль. Объявлена как volatile. Проверял - внутри всегда  присваивается true (1).  Единственно что обнаружил - при прерывании 0 реже, при прерывании 1 существенно чаще.    

Это всё было на Nano V3. На Leonardo тот же код работает без проблем.

Technolog
Offline
Зарегистрирован: 19.11.2014

nik182 LCD библиотеки не смотрел но наверное именно они содежат обработчики прерываний, которые также срабатывают в момент нажатия Вами кнопки и иногда не дают времени "зафиксировать" переменную. На другой плате просто случайно выбран пин который позволяет избежать этой накладки.

  На практике просто цепляйте нажатие кнопки не на прерывание, а на опрос пина в последовательном коде. Если есть места где есть delay(очень долго) это обычно в библиотеках DallasTemperature после команды датчикам "подготовить температуры", то :

 
void loop()

knopka_loop();   // обработка нажатий кнопок
 
  if (flag_request == 0) {k_OW_Sens.requestTemperatures(); flag_request = 1; start_pause = millis();}     // подача общей команды всем термометрам на линии подготовить температуры

  if (millis() - start_pause > 750)   // этой паузой можно регулировать период опроса (но не менее 750мс в данном случае для точности в 12 разрядов)

     {

      flag_request = 0;
     
      izmerenie();    // измерение, усреднение температур, влажности и обратных сигналов

      alarm();        // определение Алармов и их звуко-световое применение
      
      lcd_okna();     // вывод всех окон, а показывается только окно на которое указывает переменная Count_okno
      
      trans_usart();

      // и т. д ...  

      }
/*--------------------------------------------------------------------------------*/

void knopka_loop() 
{if (digitalRead(pin_Levo))                 // обработка нажатия кнопки     
    {temp_time = millis();
     while(digitalRead(pin_Levo)) {}   
     if (millis() - temp_time > 50) {Press_knopka = 1;}                // ----------- 
/*--------------------------------------------------------------------------------*/

  void izmerenie()  // измерения, вычисления
 {for (byte i = 0; i <= 12; i++)       
      {v[i] = k_OW_Sens.getTempC(a[i]); if (v[i] < 0 && v[i] > T_sensor_min) {v[i] = v[i] - 100;} knopka_loop();}
/*--------------------------------------------------------------------------------*/ 

При этом нужно подкорректировать библиотеку DallasTemperature.cpp - убрать (закомментировать) delay()

void DallasTemperature::blockTillConversionComplete(uint8_t* bitResolution, uint8_t* deviceAddress)
{
	if(deviceAddress != 0 && checkForConversion && !parasite)
	{
	  	// Continue to check if the IC has responded with a temperature
	  	// NB: Could cause issues with multiple devices (one device may respond faster)
	  	unsigned long start = millis();
		while(!isConversionAvailable(0) && ((millis() - start) < 750));	
	}
	
  	// Wait a fix number of cycles till conversion is complete (based on IC datasheet)
	  switch (*bitResolution)
	  {
	    case 9:
	      //delay(94); (закомментировано для обработки нажатия кнопки)
	      break;
	    case 10:
	      //delay(188); (закомментировано для обработки нажатия кнопки)
	      break;
	    case 11:
	      //delay(375); (закомментировано для обработки нажатия кнопки)
	      break;
	    case 12:
	    default:
	      //delay(750); (закомментировано для обработки нажатия кнопки)
	      break;
	  }
}

Как то так ...

(коды взяты из проекта работающего регулятора климата для выращивания грибов)

 

nik182
Offline
Зарегистрирован: 04.05.2015

У меня примерно так всё и работает. Но хотелось красиво. По прерыванию.

В основной программе по условию из прерывания обрабатывались две float переменные. Одна обнулялась, другой присваивалось стандартное значение. Перенёс их в прерывание. Обнулённая всегда воэвращается обнулённой. А вот той, которой присваивается значение, случайным образом возвращается обнулённой. Этого я на понимаю. Serial.print из прерывания дает нормальное значение, просле выхода из прерывания переменная иногда обнулена. Меня напрягает "иногда". Непонятен механизм обнуления и нет возможности 100% воспроизведения этого эффекта. На Leonardo ноги те же. Работает 100% правильно.    

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Прошу прощения, случайно пометил как спам последнее сообщение. С телефона иногда бывают казусы. Ещё раз, сорри, возможно админы вернут. Спасибо.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

По делу, к сожалению ничего не могу сказать. Разве что в LCD куча delayMicroseconds используется, как раз сейчас копаю это все. Если получится, будет вывод на лсиди без задержек, но там ещё пока не все как надо.

Technolog
Offline
Зарегистрирован: 19.11.2014

Все переменные обрабатываемые в прерываниях должны быть инициализированы с приставкой volatile

http://arduino.ru/Reference/Volatile

http://robocraft.ru/blog/arduino/45.html

http://alenacpp.blogspot.com/2006/04/volatile.html

Цитаты относительно использования volatile:

Описатель volatile используется в описании переменных и информирует компилятор, что значение данной переменной может быть изменено способом, который компилятор не в состоянии отследить. Для переменных, объявленных volatile, компилятор не должен применять средства оптимизации, изменяющие положение переменной в памяти (например, помещающие её в регистр) или полагающиеся на неизменность значения переменной в промежутке между двумя присваиваниями ей значения.

указывая volatile при объявлении переменной, программист просит компилятор не оптимизировать эту переменную.

Т.о. переменная получается как бы «расшарена». Т.е. значение переменной могут изменять разные части программы — обработчики прерываний, подпрограммы, функции.