реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Директива #define, Научить считать препроцессор
andrvisht
сообщение Nov 21 2005, 08:30
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064



Захотелось чтобы препроцессор вычислял значение которое нужно запихнуть в UBRR...
решил сделать вот так:
Код
#define _BAUND 9600
#define Fosc 4000000
#define BAUND (Fosc/(_BAUND*16))-1

В результате получил 176 а не 25

тогда попробовал вот так:

Код
#define _BAUND 9600
#define BAUND ((Fosc/_BAUND)/16)-1

и получил желанные 25


Но ведь записи то одинаковые, а результаты разные... ???

дальше хочу определить ошибку вот таким вариантом
Код
#define _UBRR ((Fosc/(BAUND+1))/16)
#define BAUND_ERROR ((_UBRR - _BAUND)/_BAUND)*1000

результат который хотелось видеть 1 (т.е. 0.1%) а получил 0 sad.gif

И хотелось бы выводить само значение ошибки, нашел директиву

Код
#warning любое текстовое сообщение


но оно не позволяет выводить результат расчета
типа
Код
#warning ошибка установки скорости обмена составляет BAUND_ERROR


Вообщем вопрос - как обрабатывается препроцессор и можно ли выводить на экран результат в численном виде ?
Go to the top of the page
 
+Quote Post
vet
сообщение Nov 21 2005, 08:41
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 550
Регистрация: 16-06-04
Из: Казань
Пользователь №: 32



Препроцессор не умеет считать, его функция - развернуть все определённые в программе #define-ы, включить все #include-ы и т.п. Собственно обсуждаемая константа формируется компилятором, и то, что полученное значение неверно, значит, что #include составлен неправильно.
В первом случае BAUND присваивался char-у или int-у, и компилятор привёл константы к соотв. типу, результат, конечно, получился не тот, что нужно. Правильно было бы указать тип констант явно:

Код
#define _BAUND 9600L
#define Fosc 4000000L
#define BAUND ( (Fosc/(_BAUND*16L))-1L )


Дальше не разбирался, но могу предположить, что при разворачивании одного из макросов нарушается порядок выполнения арифметических операций, что-то вроде этого:

Код
#define N 2*2 /* 4/N разворачивается в 4/2*2, т.е. 4, а не 1, как задумывалось */


Чтобы этого избежать, макросы обычно заключаются в скобки:
Код
#define N (2*2) /* 4/N разворачивается в 4/(2*2) */


--------------------
Главная линия этого опуса ясна мне насквозь!
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 21 2005, 08:53
Сообщение #3


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(&-rey @ Nov 21 2005, 14:30) *
Захотелось чтобы препроцессор вычислял значение которое нужно запихнуть в UBRR...
решил сделать вот так:
Код
#define _BAUND 9600
#define Fosc 4000000
#define BAUND (Fosc/(_BAUND*16))-1

В результате получил 176 а не 25

тогда попробовал вот так:

Код
#define _BAUND 9600
#define BAUND ((Fosc/_BAUND)/16)-1

и получил желанные 25


Но ведь записи то одинаковые, а результаты разные... ???

Ты забыл явно указать тип. А в С, если тип не указан, по умолчанию считается что тип int.
в результате эта запись: (Fosc/(_BAUND*16))-1
эквивалентна такой: (Fosc/(int)(_BAUND*16))-1
т.е. при умножении _BAUND*16 получается число 163600, что больше 65535, а затем оно обрезается до int, с получением 22528.
Чтобы этого избежать надо записть так:
Код
#define _BAUD 9600
#define Fosc 4000000
#define BAUD ((Fosc/(_BAUD*16L))-1)

Т.е. после 16 добавить букву L - указание, что умножать следует на (long)16 с получением результата типа long.
Аналогичто следует поступать, если хочешь получить рузльтат вещественного типа:
Код
#define BAUD ((Fosc/(_BAUD*16.0))-1)

16.0 - число типа double и результат выражения будет типа double.
Если написать 16.0f - будет типа float.

Сообщение отредактировал starter48 - Nov 21 2005, 09:07
Go to the top of the page
 
+Quote Post
andrvisht
сообщение Nov 21 2005, 09:37
Сообщение #4


Местный
***

Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064



Спасибо, помогло.
#define _BAUND 9600
#define Fosc 4000000
#define BAUND ((Fosc/(_BAUND*16L)))-1 = 25
#define _UBRR (Fosc/(BAUND+1))/16L - 9615
#define BAUND_ERROR (((_UBRR*10000L) - (_BAUND*10000L))/_BAUND) = 15

Вот только не понятно почему 4000000L и 4000000 воспринимаются одинаково верно, или компилятор когда видит константу автоматом присваивает ей нужный тип ?
А как все таки с выводом на экран полученного значения, возможно ли ?
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 21 2005, 09:52
Сообщение #5


Профессионал
*****

Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555



Вообще грамотнее использовать округление!
#define BAUND (unsigned)((Fosc/(_BAUND*16))-0.5)

А вот с выводом посчитанного значения проблема sad.gif
Go to the top of the page
 
+Quote Post
andrvisht
сообщение Nov 21 2005, 10:01
Сообщение #6


Местный
***

Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064



Ясно. Спасибо всем откликнувшимся... smile.gif))
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 21 2005, 11:03
Сообщение #7


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(&-rey @ Nov 21 2005, 15:37) *
Вот только не понятно почему 4000000L и 4000000 воспринимаются одинаково верно, или компилятор когда видит константу автоматом присваивает ей нужный тип ?

Да.
Цитата(&-rey @ Nov 21 2005, 15:37) *
А как все таки с выводом на экран полученного значения, возможно ли ?

Хз. У меня нет идей smile.gif
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 21 2005, 11:23
Сообщение #8


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(KRS @ Nov 21 2005, 15:52) *
Вообще грамотнее использовать округление!
#define BAUND (unsigned)((Fosc/(_BAUND*16))-0.5)

Не понял... cranky.gif
будет (unsigned)((4000000/(int)(9600*16))-0.5)
или (unsigned)(177-0.5)
или (unsigned)(176.5)
или просто 176 excl.gif
т.е. вычитание 0.5 здесь ничего не даст, т.к. все вычисления до этого целочисленные.

Может ты хотел предложить считать как вещественные числа?
вот так: #define BAUND ((unsigned)(Fosc/(_BAUND*16.0f))-1)
Можно, но осторожно, т.к. иногда при вычислении вещественных чисел иногда возникает погрешность, которая при обрезании дробной части даст ошибку на целую 1. Например вместо числа 10.0 может получится 9.999999999 - после обрезания будет 9, а не 10. Так что лучше именно целочисленную арифметику использовать.
Go to the top of the page
 
+Quote Post
KRS
сообщение Nov 21 2005, 11:57
Сообщение #9


Профессионал
*****

Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555



Цитата(starter48 @ Nov 21 2005, 14:23) *
Цитата(KRS @ Nov 21 2005, 15:52) *

Вообще грамотнее использовать округление!
#define BAUND (unsigned)((Fosc/(_BAUND*16))-0.5)

Не понял... cranky.gif
будет (unsigned)((4000000/(int)(9600*16))-0.5)
или (unsigned)(177-0.5)
или (unsigned)(176.5)
или просто 176 excl.gif
т.е. вычитание 0.5 здесь ничего не даст, т.к. все вычисления до этого целочисленные.

Может ты хотел предложить считать как вещественные числа?
вот так: #define BAUND ((unsigned)(Fosc/(_BAUND*16.0f))-1)
Можно, но осторожно, т.к. иногда при вычислении вещественных чисел иногда возникает погрешность, которая при обрезании дробной части даст ошибку на целую 1. Например вместо числа 10.0 может получится 9.999999999 - после обрезания будет 9, а не 10. Так что лучше именно целочисленную арифметику использовать.


нет именно -0.5 бывает надо округлять в другую сторону
например
для 14400 и 8 mhz
(unsigned)((8000000/(14400*16))-0.5) = 34

а если счиать в целых и вычитать 1 будет 33

8000000/(14400*16)-1=33.722222 и надо окурглять вверх


а 9.99999 получится если надо примерно 9.5 и в общем все равно куда округлять вверх или вниз.
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 21 2005, 12:34
Сообщение #10


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(KRS @ Nov 21 2005, 17:57) *
нет именно -0.5 бывает надо округлять в другую сторону
например
для 14400 и 8 mhz
(unsigned)((8000000/(14400*16))-0.5) = 34

а если счиать в целых и вычитать 1 будет 33

Согласен, но тогда допиши .0f после 16, иначе -253 будет wink.gif
Вот так: (unsigned)((8000000/(14400*16.0f))-0.5) = 34
Но как раз в таких случаях встаёт проблема с округлением о которой я говорил.
Например может вместо 10.5 в выражении (Fosc/(_BAUD*16.0f)) получиться 10.4999999 и, отняв 0.5, мы получим 9.9999999, что обрежется до 9

Цитата(KRS @ Nov 21 2005, 17:57) *
8000000/(14400*16)-1=33.722222 и надо окурглять вверх

Неа, будет -253
Чтобы получить 33.72, как в математике, надо в вещественных числах всё считать:
#define BAUD ((unsigned)(8000000/(14400*16.0f)-1))
и тогда дествительно можно отнимать 0.5 вместо 1, чтобы повысить точность при округлении.

Цитата(KRS @ Nov 21 2005, 17:57) *
а 9.99999 получится если надо примерно 9.5 и в общем все равно куда округлять вверх или вниз.

Это справедливо, если ты считаешь через long, но тогда 0.5 вычитать не имеет смысла, т.к. вычитаешь ты его от уже целого числа и вычитание 0.5 равносильно вычитанию 1.
Ведь выражение 8000000/(14400*16) - целочисленное.

Вот идея для округления в целочисленной записи:
#define BAUD ((Fosc-(_BAUD*8L))/(_BAUD*16L))

И завязывайте писать слово BAUD через N wink.gif

Сообщение отредактировал starter48 - Nov 21 2005, 12:35
Go to the top of the page
 
+Quote Post
andrvisht
сообщение Nov 24 2005, 07:25
Сообщение #11


Местный
***

Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064



Код
#define _BAUD 38400
#define _U2X 2 // Doble Speed 1 or 2
#define _UBRR (( ClkFreq-(_BAUD*8L/_U2X) )/(_BAUD*16L/_U2X))
#define __BAUD (ClkFreq/((_UBRR*16L/_U2X)+16L/_U2X))
#define _BAUD_ERROR (((__BAUD*100000L)-(_BAUD*100000L))/_BAUD)

#if (_BAUD_ERROR >= 1000L)||(_BAUD_ERROR <= -1000L) // тысячные доли процента
#warning ошибка установки составляет _BAUD_ERROR
#endif

И все вроде хорошо только вот _BAUD_ERROR по умолчанию int и компилятор сообщает

Warning[Pe061]: integer operation result is out of range

если сделать попытку приведения типа
Код
#if ((long)_BAUD_ERROR >= 1000L)||((long)_BAUD_ERROR <= -1000L) // тысячные доли процента
#warning ошибка установки составляет _BAUD_ERROR
#endif


то вообще ругается
Error[Pe059]: function call is not allowed in a constant expression

Как быть ?
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 24 2005, 09:32
Сообщение #12


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(&-rey @ Nov 24 2005, 13:25) *
И все вроде хорошо только вот _BAUD_ERROR по умолчанию int и компилятор сообщает

Warning[Pe061]: integer operation result is out of range

Как быть ?

Я проверил эту конструкцию на Borland C. Нормально работает.
Похоже, препроцессор IAR сравнивать умеет только целые числа. (long) не поможет.
Считай в сотых долях процента, чтобы в диапазон целых чисел входило:
Код
#define _BAUD (38400L)
#define _U2X (2) // Doble Speed 1 or 2
#define ClkFreq (4000000L)
#define _UBRR (( ClkFreq-(_BAUD*8L/_U2X) )/(_BAUD*16L/_U2X))
#define __BAUD (ClkFreq/( (_UBRR+1L) *16L/_U2X) )
#define _BAUD_ERROR (((__BAUD-_BAUD)*10000L))/_BAUD)

#if (_BAUD_ERROR >= 100L)||(_BAUD_ERROR <= -100L) // сотые доли процента
#error погрешность установки скорости посл. порта более 1%
#endif


У меня тут предложение возникло...
А почему бы тебе не поставить кварц на 11.059MHz? Тогда стандартные скорости получишь без погрешности.
Только не говори, что 4000000 - частота внутр. RC генератора wink.gif
У меня нестабильно работало, когда я пытался от RC тактировать контроллер с посл. портом, т.к. термостабильность низкая у RC. Т.е. при прогреве контроллера или изменении напряжения питания частота RC уходила.

Сообщение отредактировал starter48 - Nov 24 2005, 09:43
Go to the top of the page
 
+Quote Post
andrvisht
сообщение Nov 24 2005, 10:13
Сообщение #13


Местный
***

Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064



Цитата(starter48 @ Nov 24 2005, 13:32) *
У меня тут предложение возникло...
А почему бы тебе не поставить кварц на 11.059MHz? Тогда стандартные скорости получишь без погрешности.

Конечно, я тоже не экономлю на кварцах smile.gif) Просто на C недавно перешел и перевожу свои библиотеки под IAR. Поэтому раз уж С то решил сделать типа универсального.
По поводу сравнений идея понятна, а что это IAR по поводу функции говорит ? Что у него на уме ?

И вот еще такой вопрос:
Захотел попробовать в качестве битов использовать битовое поле:
Код
typedef struct {// Структура битового поля
unsigned char bit0:1;
unsigned char bit1:1;
unsigned char bit2:1;
unsigned char bit3:1;
unsigned char bit4:1;
unsigned char bit5:1;
unsigned char bit6:1;
unsigned char bit7:1;
}bitfield;
static bitfield UARTstat={0,0,0,0,0,0,0,0};

и вот вопрос:

Возможно ли обратиться к структуре чтобы разом установить несколько битов ?
Просто ведь у IAR обращение например к порту можно сделать:
Код
PORTB_Bit0 = 1;
или
PORTB = 1<<PB0;


Или при таких желаниях мне лучьше подойдут Битовые маски ?
Go to the top of the page
 
+Quote Post
Rash
сообщение Nov 24 2005, 10:21
Сообщение #14


Знающий
****

Группа: Свой
Сообщений: 639
Регистрация: 5-09-05
Пользователь №: 8 231



Цитата
Возможно ли обратиться к структуре чтобы разом установить несколько битов ?



// ????????????? ??????? ????? ??? ??? ???????
// ?????????? ??????? ??????, (?? ????? ?? ?????? ?????-??????)
#define SET_BITS(Reg, Bit) (Reg |= (Bit)) // ????????? ????? ? ?????
#define CLEAR_BITS(Reg, Bit) (Reg &= (~Bit)) // ????? ????? ? ?????
#define CHECK_BIT(Reg, Bit) (Reg & (Bit)) // ???????? ???? ? ?????

#define SET_BIT(Reg, Bit) Reg |= (1<<Bit) // ?????????? ???
#define CLEAR_BIT(Reg, Bit) Reg &= ~(1<<Bit) // ???????? ???
#define INVER_bit(Reg, Bit) Reg ^= (1<<Bit ) // ????????????? ???
#define GET_BIT(Reg, Bit) (Reg & (1<<Bit)) // ????????? ???
Go to the top of the page
 
+Quote Post
starter48
сообщение Nov 24 2005, 10:36
Сообщение #15


Частый гость
**

Группа: Свой
Сообщений: 112
Регистрация: 15-10-05
Из: Томск
Пользователь №: 9 680



Цитата(&-rey @ Nov 24 2005, 16:13) *
По поводу сравнений идея понятна, а что это IAR по поводу функции говорит ? Что у него на уме ?

То же самое. Вообще, работать должно smile.gif
Видать препроцессор у IAR не поддерживает явное указание типа.
Может у него опции есть какие-нибудь для включения совместимости.
Я не знаю. Почитай документацию в каталоге avr\doc.
Цитата(&-rey @ Nov 24 2005, 16:13) *
И вот еще такой вопрос:
Захотел попробовать в качестве битов использовать битовое поле:
и вот вопрос:
Возможно ли обратиться к структуре чтобы разом установить несколько битов ?
Просто ведь у IAR обращение например к порту можно сделать:
Код
PORTB_Bit0 = 1;
или
PORTB = 1<<PB0;


Или при таких желаниях мне лучьше подойдут Битовые маски ?

Посмотри как PORT в .h задаётся.
там даже специально дан пример:
Код
union {
  unsigned char AVR;
  struct {
    unsigned char AVR_Bit0:1,
    AVR_Bit1:1,
    AVR_Bit2:1,
    AVR_Bit3:1,
    AVR_Bit4:1,
    AVR_Bit5:1,
    AVR_Bit6:1,
    AVR_Bit7:1;
  };
};

Здесь к переменной AVR можно обращаться как к char: AVR=111,
а можно через биты: AVR_Bit5=1
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 16:33
Рейтинг@Mail.ru


Страница сгенерированна за 0.01487 секунд с 7
ELECTRONIX ©2004-2016