Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблема с volatile переменной - помогите плиз.
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
Илья_
Добрый день господа форумчане.

У меня возникла проблема - очень надеюсь на вашу помощь.

Пишу программу под PIC16F876 на PICC от HT-PICC.

В программе захотел сделать функцию антидребезг.
unsigned char do_antiripple_pin(volatile struct data_unit * data , char time_conversion);
В эту функцию передаю время проверки на дребезг и
указатель на структуру в которой содержиться иформация о ножке процессора.

volatile struct data_unit
{
volatile unsigned char pin_addres; // Адрес пина на цоколевке процессора.
unsigned char time_to_end; // Показывает сколько времени до конца преобразования
unsigned char temp_pin_value; // Сохраняет временное значение на меняющимся пине проца.
unsigned char changing_data; // Переменная с которой прграмма работает как с ножкой проца
unsigned char diferences_counter; // Счетчик несовпадений пина и значения переменой за интервал
};

Проблема с полем pin_addres - это у меня просто имя пина ( допустим RA0), я объявил глобальный экземпляр структуры volatile struct data_unit button_data = {0,0,1,1,0};потом в main(); присвоил полю pin_addres значение RA0

button_data.pin_addres = RA0;

ниже вызываю функцию do_antiripple_pin(&button_data,100);
и надеюсь что у меня в функцию передасться значение с ножки порта, причем не просто значение
а мгновенное, так как я везде где можно поставил volatile.

Но этого не происходит. Программа как один раз зафиксировала в памяти значение

button_data.pin_addres = RA0;

так больше его не меняет, хотя значение на ножке меняется (это я вижу через IDC2).

Выкладываю программу , там в листинге более понятно, чем я тут объясняю.

Программма как следует не работает, но если закоментировать стр 31, и раскоментировать стр 92
тогда все работает нормально. Нормально так же все работает если передавать в функцию просто
назвние ножки порта.

Но вот почему не работает как мне надо??? (Т.е в том виде в котором выложил!)

Помогите пожалуйста разобраться.
rezident
При вызове функции do_antiripple_pin квалификатор volatile относится непосредственно к указателю, а не к полям структуры, указатель на которую передается. Я не пойму зачем вам этот геморрой с указателями, если у вас структура глобальная? Обращайтесь в функции do_antiripple_pin непосредственно к ее полям, типа
Код
data_unit.temp_pin_value = data_unit.pin_addres;

Если же вы хотите разнести работу со структурой по разным модулям, то создайте новый тип со всеми нужными volatile квалификаторами. И используйте этот тип в применении как к структурам, так и к указателям на них.
Код
#pragma pack(1)
typedef struct data_unit_t
{ volatile unsigned char  pin_addres;            // Адрес пина на цоколевке процессора.
  unsigned char             time_to_end;        // Показывает сколько времени до конца преобразования
  unsigned char             temp_pin_value;        // Сохраняет временное значение на меняющимся пине проца.
  unsigned char             changing_data;        // Переменная с которой прграмма работает как с ножкой проца
  unsigned char             diferences_counter;    // Счетчик несовпадений пина и значения переменой за интервал
} data_unit_t;    
#pragma pack()

объявление структуры
Код
data_unit_t        button_data = {0,0,1,1,0};

функция
Код
unsigned char do_antiripple_pin(data_unit_t * data , char time_conversion)
{
...
}

А вообще, советую вам пересмотреть алгоритм и отказаться от бездумного использования квалификатора volatile где попало.
Илья_
Уважаемый rezident.

Я попробовал сделать как вы советовали. Результат тот же - ничего не получается. Поле pin_addres не обновляеися согласно со значением ножки RA0.

А вот насчет того что истользовать volatile где попало не следует, я с вами согласен.

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

И еще я передаю в функцию только указатель на структуру, что бы избежать лишнего копирования при передачи аргументов в функцию.

Но главный вопрос так и остался открытым :
"Почему переменная обьявлення как volatile и которой присвоено значение ножки процессора, на меняется согласно с изменением сигнала на этой ноге процессора ??? "

Прикладываю новую версию программв с изменениями которые советовал внести г-н rezident.
rezident
Цитата(Илья_ @ Aug 20 2008, 12:46) *
Но главный вопрос так и остался открытым :
"Почему переменная обьявлення как volatile и которой присвоено значение ножки процессора, на меняется согласно с изменением сигнала на этой ноге процессора ??? "
Да потому, что вы никак не можете понять, что указатель это тоже переменная, которая содержит адрес переменной к которой вы обращаетесь с помощью этого указателя. Так вот значение адреса не меняется. Меняется содержимое переменной, которую вы адресуете с помощью указателя. Так что квалификатор volatile в определении указателя никак не влияет на считывание значения переменной с помощью этого указателя.
Пример.
Имеем условный 8-ми битный порт M по адресу 0x0010.
Его определение в хидерах скорее всего такое
portsdef.h
Код
#ifndef PORTSDEF_H
#define PORTSDEF_H
__no_init volatile unsigned char PORT_M @ 0x0010;
#endif

main.c
Код
#include "portsdef.h"

#define BIT0 0x01

unsigned char *pPortM;

void main(void)
{ pPortM=(unsigned char *)&PORT_M;
  ...
  if ((*pPortM&BIT0)==0) //Проверяем состояние первого бита порта M
  {
    ...
  }
  ...
}

Вне зависимости от того, как мы объявили указатель
Код
unsigned char *pPortM;

или
Код
volatile unsigned char *pPortM;

У нас содержимое самого указателя не меняется и всегда равно 0x0010. А меняется содержимое порта PORT_M по адресу 0x0010, адрес которого содержит указатель.
Так, что для считывания реальных данных с порта нужно использовать специальную конструкцию заложенную компилятором (см. выше) и его непосредственный адрес.
Код
if ((PORT_M&BIT0)==0)
{
  ...
}
Либо попробовать симмитировать ее, как-то так
Код
if ((((volatile unsigned char)(*pPortM))&BIT0)==0)
{
  ...
}
sergeeff
Цитата(Илья_ @ Aug 20 2008, 10:46) *
Уважаемый rezident.

Я попробовал сделать как вы советовали. Результат тот же - ничего не получается. Поле pin_addres не обновляеися согласно со значением ножки RA0.

А вот насчет того что истользовать volatile где попало не следует, я с вами согласен.

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

И еще я передаю в функцию только указатель на структуру, что бы избежать лишнего копирования при передачи аргументов в функцию.

Но главный вопрос так и остался открытым :
"Почему переменная обьявлення как volatile и которой присвоено значение ножки процессора, на меняется согласно с изменением сигнала на этой ноге процессора ??? "

Прикладываю новую версию программв с изменениями которые советовал внести г-н rezident.


Уважаемый Илья!

Поймите раз и навсегда. Переменная (или поле в структуре) - это значение, лежащее в некоторой ячейке памяти. Указатель на эту переменную - адрес этой ячейки памяти. Улавливаете, что никакими ножками порта тут не пахнет?

Значит надо обеспечить программно чтение значения этой ноги и ее сохранение в вашей переменной. Volatile к этому процессу ну никаким образом. Volatile лишь запрещает компилятору оптимизировать эту переменную.
Опрашивать состояние ножки можно либо в каком-либо программном цикле, либо по прерыванию, если можно организовать такое, по изменению значения на ноге (зависит от конкретного процессора)
Илья_
Уважаемые господа, sergeeff и rezident.

То что указатель содержит адрес ячейки памяти, в которой храниться значение переменной
на которую этот указатель указывае - это азы Си. И они мне уверяю вас известны.

А то что модификатор voltile в обьявлениии указателя может указывать что указатель, так сказать волатильный( т.е. может измениться не только из программы, как регистры переферии ), и может
модификатор voltile указывать что указатель указывае на волатильную перменную.

Вот цитата из прервода документации по компилятору HT-PICC:


"Чтобы четче определить поведение и особенности указателя и объекта на который он
указывает можно комбинировать различные квалификаторы. К наиболее часто употребимым
квалификаторам относятся const, volatile, persistent. Применяя комбинации этих
квалификаторов необходимо следить за соответствием (отсутствием противоречий)
квалификаторов, оказывающих воздействие на свойства собственно указателя и объекта на
котрый он указывает. Правила, которые помогут сделать все правильно, просты: если
квалификатор находится слева от “*” в описании указателя, то он воздействует на объект,
адресуемый указателем. Если квалификатор находится справа, то он воздействует на
собственно указатель. Проиллюстрируем это примерами:
volatile char * nptr;
объявляет указатель на volatile символ. Другими словами в этом примере квалификатор
воздействует на объект, адресуемый указателем nptr.
char * volatile ptr;
так как квалификатор располагается справа от “*”, то он будет воздействовать на
собственно указатель ptr, а не на объект. И заключительный пример по этому поводу:
volatile char * volatile nnptr;
теперь будет описан volatile указатель на volatile переменную.
Рассмотрим некоторые аспекты применения константных указателей. Они применяются
для косвенного обращения к переменным описанным как const. В общем их поведение
(константных указателей) не отличается от поведения обычных указателей, но компилятор
препятствует выполнению операций записи с использованием этих указателей. Вот
несколько примеров:
const char * cptr;
при этом выражение:
char ch = *cptr;
абсолютно легально, в то время как выражение:
*cptr = ch;
недопустимо и вызовет ошибку"


Судя по тому что написано в описании, я совершенно правильно использовал квалификатор volatile,
а то что сам указатель не меняется так это тоже верно.

А я написал функцию которая выполняет антидребезг. И передаю в нее время , в течении которого проводить проверку на дребезг, и так же передаю в функцию структуру которая содержит информацию о пине. Так как антидребезг мы можем производить для любой из ножек контроллера к которой подклюен датчик или кнопка.

Так вот я мог бы пердавать в функцию явно номер пина RA0 или RC5 и это бы работало.
А если передавать структуру, в которой одним из полей будет номер пина (RA0 или RC5)
то функция работает некоретно. И вот вопрос ПОЧЕМУ???

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

Хотя быть может вы меня переубедите, если предложите другие аргументы. smile.gif
OlegH
Цитата(Илья_ @ Aug 19 2008, 17:21) *
В эту функцию передаю время проверки на дребезг и
указатель на структуру в которой содержиться иформация о ножке процессора.

volatile struct data_unit
{
volatile unsigned char pin_addres; // Адрес пина на цоколевке процессора.
unsigned char time_to_end; // Показывает сколько времени до конца преобразования
unsigned char temp_pin_value; // Сохраняет временное значение на меняющимся пине проца.
unsigned char changing_data; // Переменная с которой прграмма работает как с ножкой проца
unsigned char diferences_counter; // Счетчик несовпадений пина и значения переменой за интервал
};

Проблема с полем pin_addres - это у меня просто имя пина ( допустим RA0), я объявил глобальный экземпляр структуры volatile struct data_unit button_data = {0,0,1,1,0};потом в main(); присвоил полю pin_addres значение RA0

button_data.pin_addres = RA0;

ниже вызываю функцию do_antiripple_pin(&button_data,100);
и надеюсь что у меня в функцию передасться значение с ножки порта, причем не просто значение
а мгновенное, так как я везде где можно поставил volatile.

Но этого не происходит. Программа как один раз зафиксировала в памяти значение


Уважаемый Илья smile.gif volatile тут вообще не причем smile.gif ваша программа работает строго так, как вы написали.

"volatile unsigned char pin_addres " - Какой же это адрес ?!? Если адрес должен выглядеть как
"volatile unsigned char * pin_addres".


"button_data.pin_addres = RA0;" - как раз ОДИН РАЗ присваивает байтовой переменной pin_addres (которая адресом только называется и застряла в вашем воображении) ЗНАЧЕНИЕ пина RA0.
а должно быть что-то вроде

"button_data.pin_addres = &RA0;".

Отсюда вывод - учить букварь по Си. А еще лучше учиться писать на Ассемблере, чтобы начать действительно понимать Си smile.gif


А вообще (я именно про PIC не в курсе) - возможно для PIC такое косвенное обращение к портам ввода-вывода вообще невозможно, либо делается другими средствами.
rezident
Цитата(Илья_ @ Aug 21 2008, 02:15) *
Хотя быть может вы меня переубедите, если предложите другие аргументы. smile.gif
Аргумент у меня только один, но зато он железобетонногранитнобронзовый - ваша программа НЕ РАБОТЕТ так, как вам нужно. wink.gif
Кстати, вы так и не привели выдержку о том, что представляет из себя символьное обозначение RA0.
Сергей Борщ
Цитата(rezident @ Aug 20 2008, 23:51) *
Кстати, вы так и не привели выдержку о том, что представляет из себя символьное обозначение RA0.
Дедуктивно предположу, что это разыменованный указатель на volatile переменную типа bit. Этот тип является расширением языка HI-TECH компилятора для пиков. И если компилятор позволяет делать указатели на такие типы (в чем я сомневаюсь, зная систему команд пика), то поле структуры должно быть объявлено как bit volatile * pin_address; или volatile bit * pin_address;, что одно и то же.
rezident
Цитата(Сергей Борщ @ Aug 21 2008, 03:26) *
то поле структуры должно быть объявлено как bit volatile * pin_address; или volatile bit * pin_address;, что одно и то же.
А это как-то поможет с реализацией задуманного автором при использовании его структуры?
ReAl
Цитата(rezident @ Aug 21 2008, 01:06) *
А это как-то поможет с реализацией задуманного автором при использовании его структуры?

Если бы архитектура пиков поддерживала указатели на биты (о чём Сергей и сказал), то помогло бы.
Просто надо было бы так.
Код
unsigned char do_antiripple_pin(struct data_unit * data , char time_conversion)
{
...
        if( data->changing_data != *data->pin_addres)    // И если новые данные отличаются от старых    
        {
            data->temp_pin_value = *data->pin_addres;    // то сохраняем временные данные;
            data->diferences_counter++;                // и увеличиваем счетчик несовпадений пина    
        }


Аналогично и Ваш пример
Цитата(rezident @ Aug 20 2008, 18:29) *
Либо попробовать симмитировать ее, как-то так
Код
if ((((volatile unsigned char)(*pPortM))&BIT0)==0)
{
  ...
}

можно изменить так
Код
if ( ( (*(volatile unsigned char*)pPortM) & BIT0) ==0)
{
  ...
}
Или вообще сразу объявить pPortM как volatile unsigned char *



Тут достаточно адресуемости всего порта, но номер ножки придётся передавать отдельно и делать маску на месте.
Так что автору вопроса надо что-то в духе
Код
struct data_unit
{    
    volatile unsigned char  *port_address;
    unsigned char pin_number;
    // Дальше остальные поля, не факт, что все они должны быть volatile, это надо смотреть
    // Всю структуру делать volatile врядли надо, это как минимум сделает volatile и сам port_address, что
    // явно не нужно. Хотя, если сделать лишь одно обращение к полю port_address, как это сделано
    // ниже, квалифицирование port_address как volatile не приведёт к раздуванию кода
        ...
};    

......
        unsigned char new_state = 0;

        if( *data->port_address & (1 << data->pin_number) )
                new_state = 1;

        if( data->prev_state != new_state) {
              data->temp = new_state;
              ...
             // ну и дальше по вкусу, я не вникал в тонкости именно этого варианта подавления дребезга
AHTOXA
Можно ещё вот так:

Код
typedef int (* ReadStateFunc)(void); // прототип функции для чтения состояния ноги

// структура ноги
typedef struct
{
  ReadStateFunc ReadState;
  ...

}PinData;

// функция чтения ноги A0 (придётся написать для каждой ноги)
int ReadPinA0(void)
{
  if (RA0) return 1;
  return 0;
}

// нога A0
PinData PinA0 = {ReadPinA0...};

// функция антидребезга.
int debounce(PinData * pin, ...)
{
}
ReAl
Цитата(AHTOXA @ Aug 21 2008, 13:56) *
Можно ещё вот так:

Код
typedef int (* ReadStateFunc)(void); // прототип функции для чтения состояния ноги

// структура ноги
typedef struct
{
  ReadStateFunc ReadState;
  ...

}PinData;

...
Можно и так. Причём в зависимости от архитектуры и прочего может оказаться, что это будет работать даже быстрее (косвенный вызов против сдвига в цикле деинички для создания маски).
Что характерно, после этого возникает вопрос - а что там за споры были про С++? Неужели С-шная ручная эмуляция виртуальной функции опроса вывода кардинально эффективнее встроенной в язык поддержки? wink.gif


Она будет эффективнее, так как в С++ идёт рассчёт на возможное большое количество таких функций и в каждом экземпляре хранятся не непосредственно указатели на функции, а указатель на таблицу виртуальных функций класса, будет не просто выборка из структуры адреса функции и вызов, а выборка указателя на таблицу, выборка из неё указателя на функцию и только потом вызов.
Но для большинства архитектур это не будет жутким перерасходом ресурсов.
AHTOXA
Цитата(ReAl @ Aug 21 2008, 18:01) *
Можно и так. Причём в зависимости от архитектуры и прочего может оказаться, что это будет работать даже быстрее (косвенный вызов против сдвига в цикле единички для создания маски).


Ну, маску можно сделать членом структуры:-) Но выигрыш будет всё равно мизерный.

Цитата
Что характерно, после этого возникает вопрос - а что там за споры были про С++?


Это не ко мне, я-то как раз за Си++ :-)
Илья_
Добрый день господа форумчане.

Ваши советы, деийствительно дельные и очень помогают. Всем большое спасибо за это.

Отдельное спасибо форуму за то что, объединяет профессоналов и людей интересующихся электроникой и является местом где можно изложить свою проблему. А грамотно изложив проблему людям с сам уже на полпути продвиншься к решению. Ибо не даром сказано "Кто ясно мыслит - тот ясно излагает." И чего следует, что надо время от времени ясно излагать что ды достичь ясности в мыслях....

Извините, за лиричиское отступление wacko.gif (Остапа, как говориться, понесло....) smile.gif


Итак к делу:

1) Для большей ясности для начала отвечаю на вопрос г-на residenta:
"Кстати, вы так и не привели выдержку о том, что представляет из себя символьное обозначение RA0."

Вот ответ: static volatile bit RA0 @ (unsigned)&PORTA*8+0;
static volatile unsigned char PORTA @ 0x05;

и еще выдержка из описания к компилятору:
"2.9 Статические переменные
Глобальные или статические переменные могут располагаться по абсолютному адресу.
Для этого в описании переменной используется специальная конструкция “@ <адрес>”.
Например:
volatile unsigned char Portvar @ 0x06;
Такое описание дает возможность обращаться к абсолютному адресу 0х06 посредствам
переменной Portvar. Надо учитывать, что в этом случае компилятор не резервирует память
под размещение этой переменной. С точки зрения ассемблера это описание выглядит
следующим образом:
Portvar equ 06h"


2) Прав г-н Олег Хохлов написав:
""volatile unsigned char pin_addres " - Какой же это адрес ?!? Если адрес должен выглядеть как
"volatile unsigned char * pin_addres"."

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

3) И вот тут я подошел к тому о чем писал г-н Сергей Борщ :
"И если компилятор позволяет делать указатели на такие типы (в чем я сомневаюсь, зная систему команд пика), то поле структуры должно быть объявлено как bit volatile * pin_address; или volatile bit * pin_address;, что одно и то же"
Да, компилятор не прозволяет делать указатели на переменные типа бит и следует, ввести структуру
поле - адрес номера порта, и поле - номер бита в порте, тогда проблема решается. Но об этом же
написал г-н ReAl a14.gif

И вот тут я пришел к тому, что нехочется мне в структру вводить дополнительное поле и я решил сделать по другому - см. прикрепленные файлы.

Все в таком работает отлично, только мне опять приходиться опять перед вызовом функции антидребезг обновлять значение ножки порта в структуре. Как бы мне от этого избавиться
, что бы один раз(при инициализаци) связать значение ножки порта и поле структуры?

Г-ну AHTOXе хочу сказать что его вариант конечно интересный, но писать одинаковые функции для каждой ножки порта я нахожу громоздким. А если ножек 80 как в PIC18f87j10? Я же хочу написать универсальную функцию.
AHTOXA
Цитата(Илья_ @ Aug 22 2008, 13:32) *
Г-ну AHTOXе хочу сказать что его вариант конечно интересный, но писать одинаковые функции для каждой ножки порта я нахожу громоздким. А если ножек 80 как в PIC18f87j10? Я же хочу написать универсальную функцию.


Неужели на все 80 ножек нужен антидребезг? wacko.gif И потом, зачем всё писать вручную? Есть же макросыsmile.gif

Код
typedef int (* ReadStateFunc)(void);

typedef struct
{
  ReadStateFunc ReadState;
  int ticks;
  int old_state;
}PinData;

#define PIN_DEBOUNCE_TIME    (10)

#define DECLARE_PIN(pin) \
int ReadPin##pin(void) \
{ \
  if (pin) return 1; \
  return 0; \
} \
\
PinData Pin##pin = {ReadPin##pin}\

DECLARE_PIN(B0);
DECLARE_PIN(B1);
DECLARE_PIN(B2);

int debounce(PinData * pin)
{
    int pstate = pin->ReadState();
    if (pstate != pin->old_state)
    {
        pin->ticks = PIN_DEBOUNCE_TIME;
        pin->old_state = pstate;
    }
    else if (pin->ticks)
        pin->ticks--;
    return pin->ticks;
}

void test(void)
{
    debounce(&PinB0);
    debounce(&PinB1);
}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.