|
Директива #define, Научить считать препроцессор |
|
|
|
Nov 21 2005, 08:30
|
Местный
  
Группа: Свой
Сообщений: 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  И хотелось бы выводить само значение ошибки, нашел директиву Код #warning любое текстовое сообщение но оно не позволяет выводить результат расчета типа Код #warning ошибка установки скорости обмена составляет BAUND_ERROR Вообщем вопрос - как обрабатывается препроцессор и можно ли выводить на экран результат в численном виде ?
|
|
|
|
|
Nov 21 2005, 08:41
|
Знающий
   
Группа: Свой
Сообщений: 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) */
--------------------
Главная линия этого опуса ясна мне насквозь!
|
|
|
|
|
Nov 21 2005, 08:53
|

Частый гость
 
Группа: Свой
Сообщений: 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
|
|
|
|
|
Nov 21 2005, 11:03
|

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

|
Цитата(&-rey @ Nov 21 2005, 15:37)  Вот только не понятно почему 4000000L и 4000000 воспринимаются одинаково верно, или компилятор когда видит константу автоматом присваивает ей нужный тип ? Да. Цитата(&-rey @ Nov 21 2005, 15:37)  А как все таки с выводом на экран полученного значения, возможно ли ? Хз. У меня нет идей
|
|
|
|
|
Nov 21 2005, 11:23
|

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

|
Цитата(KRS @ Nov 21 2005, 15:52)  Вообще грамотнее использовать округление! #define BAUND (unsigned)((Fosc/(_BAUND*16))-0.5) Не понял... будет (unsigned)((4000000/(int)(9600*16))-0.5) или (unsigned)(177-0.5) или (unsigned)(176.5) или просто 176 т.е. вычитание 0.5 здесь ничего не даст, т.к. все вычисления до этого целочисленные. Может ты хотел предложить считать как вещественные числа? вот так: #define BAUND ((unsigned)(Fosc/(_BAUND*16.0f))-1) Можно, но осторожно, т.к. иногда при вычислении вещественных чисел иногда возникает погрешность, которая при обрезании дробной части даст ошибку на целую 1. Например вместо числа 10.0 может получится 9.999999999 - после обрезания будет 9, а не 10. Так что лучше именно целочисленную арифметику использовать.
|
|
|
|
|
Nov 21 2005, 11:57
|

Профессионал
    
Группа: Модераторы
Сообщений: 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)
Не понял... будет (unsigned)((4000000/(int)(9600*16))-0.5) или (unsigned)(177-0.5) или (unsigned)(176.5) или просто 176 т.е. вычитание 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 и в общем все равно куда округлять вверх или вниз.
|
|
|
|
|
Nov 21 2005, 12:34
|

Частый гость
 
Группа: Свой
Сообщений: 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 будет  Вот так: (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
Сообщение отредактировал starter48 - Nov 21 2005, 12:35
|
|
|
|
|
Nov 24 2005, 07:25
|
Местный
  
Группа: Свой
Сообщений: 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 Как быть ?
|
|
|
|
|
Nov 24 2005, 09:32
|

Частый гость
 
Группа: Свой
Сообщений: 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 генератора  У меня нестабильно работало, когда я пытался от RC тактировать контроллер с посл. портом, т.к. термостабильность низкая у RC. Т.е. при прогреве контроллера или изменении напряжения питания частота RC уходила.
Сообщение отредактировал starter48 - Nov 24 2005, 09:43
|
|
|
|
|
Nov 24 2005, 10:13
|
Местный
  
Группа: Свой
Сообщений: 298
Регистрация: 29-08-05
Пользователь №: 8 064

|
Цитата(starter48 @ Nov 24 2005, 13:32)  У меня тут предложение возникло... А почему бы тебе не поставить кварц на 11.059MHz? Тогда стандартные скорости получишь без погрешности. Конечно, я тоже не экономлю на кварцах  ) Просто на 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; Или при таких желаниях мне лучьше подойдут Битовые маски ?
|
|
|
|
|
Nov 24 2005, 10:36
|

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

|
Цитата(&-rey @ Nov 24 2005, 16:13)  По поводу сравнений идея понятна, а что это IAR по поводу функции говорит ? Что у него на уме ? То же самое. Вообще, работать должно  Видать препроцессор у 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
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|