Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: #define DELAY(CY)
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
ViKo
Конкретно, хочу, чтобы
DELAY(1) превратилось в
__nop()
DELAY(2)
__nop();
__nop()
...
DELAY(20)
for(uint32_t i = 4; i--; ) __nop()
Варианты найдутся. Вопрос, как их задать в одном макроопределении?
Сергей Борщ
Код
#define DELAY(cyсles) if((cycles) == 1) asm volatile("nop"); else if((cycles)==2) { asm volatile ("nop\n\tnop"); } else ....
Если параметр константа и включена хотя бы минимальная оптимизация - компилятор выкинет проверку и лишние ветки.
demiurg_spb
Цитата(Сергей Борщ @ Apr 9 2014, 09:34) *
Если параметр константа и включена хотя бы минимальная оптимизация - компилятор выкинет проверку и лишние ветки.
В кейле не прокатывало (пару лет назад пробовал). В GCC да.
ViKo
Код
#define DELAY_CY(CY) \
  if (CY == 1)       \
    __nop()           \
  else if (CY == 2) {  \
    __nop();          \
    __nop();           \
  }

Получается в Кейле. При всех оптимизациях.
Хотел было рекурсивный вызов макро сделать. Нельзя.
demiurg_spb
Вот это в GCC отлично рабртает, а в кейле, когда я пробовал - облом...
Интересно, с тех пор что-нибудь изменилось?
Код
//=============================================================================
static __inline void delay_8cycles(uint32_t x)
{
/*
    subs r0
    bcs  exit

    mrs r0, primask
exit:
    bx lr

*/

    while (x--) // 4  SUBS + BCS + PIPLINE_RELOAD
    {
        __NOP(); //    5
        __NOP(); //    6
        __NOP(); //    7
        __NOP(); //    8
    }
}

//=============================================================================
static __inline void delay_cycles(uint32_t x)
{
    uint32_t rem = x&7; // x%8

    if      (rem==1)  {__NOP();}
    else if (rem==2)  {__NOP();__NOP();}
    else if (rem==3)  {__NOP();__NOP();__NOP();}
    else if (rem==4)  {__NOP();__NOP();__NOP();__NOP();}
    else if (rem==5)  {__NOP();__NOP();__NOP();__NOP();__NOP();}
    else if (rem==6)  {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
    else if (rem==7)  {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}

    if ((x/=8))       delay_8cycles(x);
}

//=============================================================================
static __inline void delay_ns  (double ns)    {delay_cycles(ns/((double)1e9/(double)F_CPU) + 0.5);}
void                 delay_us  (uint32_t us);
void                 delay_ms  (uint32_t ms);
void                 delay_sec (uint32_t sec);
Сергей Борщ
Цитата(demiurg_spb @ Apr 9 2014, 08:40) *
В кейле не прокатывало (пару лет назад пробовал). В GCC да.
Пробовал лет 8 назад в ИАРе - там тоже работало. Неужели кейл не умел выкидывать мертвый код? У меня о нем тогда сложилось довольно хорошее впечатление, хоть и работал с ним совсем недолго.
demiurg_spb
Цитата(Сергей Борщ @ Apr 9 2014, 11:02) *
Пробовал лет 8 назад в ИАРе - там тоже работало. Неужели кейл не умел выкидывать мертвый код? У меня о нем тогда сложилось довольно хорошее впечатление, хоть и работал с ним совсем недолго.
Сам был неприятно удивлён. Также как и отсутствием возможности вычисления на этапе компиляции cos(const) и т.д.
Именно это меня и сподвигло перейти на gcc и на arm платформе.
Пробовал на ARM C/C++ Compiler, 4.1
ViKo
Сотворил.
CODE

/*!****************************************************************************
@brief Delay program loop
@param NS - время задержки в ns
@note Параметр округляется до ближайшего не меньшего, кратного тактам
@note 5 тактов в цикле при оптимизации -O3 (-Otime), -O2
6 тактов в цикле при оптимизации -O1 и -O0
+ 1 такт перед циклом при оптимизации -O0
@note При проверке условия выхода из цикла компиприлятор отбрасывает
одиночный цикл, поэтому счет циклов начинается с 2
@note Формула: D = 5 * (CY5 + 1) + Nnop; 6 * (CY5 + 1) + Nnop (+ 1)
*/
#define SYSCLK_MHZ 72
#define NS2CY(NS) ((NS * SYSCLK_MHZ + 999) / 1000)

#define DELAY_NS(NS) DELAY_CY(NS2CY(NS))
#define DELAY_CY(CY) \
if (CY % 5 == 1) \
{ __nop(); } \
else if (CY % 5 == 2) \
{ __nop(), __nop(); } \
else if (CY % 5 == 3) \
{ __nop(), __nop(), __nop(); } \
else if (CY % 5 == 4) \
{ __nop(), __nop(), __nop(), __nop(); } \
if (CY > 4 && CY < 10) \
{ __nop(), __nop(), __nop(), __nop(), __nop(); } \
else if (CY >= 10) \
{ __nop(); uint32_t CY5 = (CY - 5) / 5; do { __nop(); } while (CY5--); }
Shamil
Надо бы, наверное, весь макрос поместить внутрь блока, чтобы вставлялся как один оператор,
иначе, если применить его после if, будут неприятности.
Код
do
{
    Текст макроса.
}
while(0)

ViKo
Цитата(Shamil @ Apr 10 2014, 04:18) *
Надо бы, наверное, весь макрос поместить внутрь блока, чтобы вставлялся как один оператор,
иначе, если применить его после if, будут неприятности.
Код
do
{
    Текст макроса.
}
while(0)

Помню-помню... разговоры. Но я не вставляю после if.
P.S. у меня же все ветки в скобках { }, может, их хватит? rolleyes.gif

Другой вопрос всплыл - Как можно определить, с какими параметрами оптимизации задана копмиляция? Что можно было в одном случае в формулу подсунуть 5, а в другом 6.
Сергей Борщ
Цитата(ViKo @ Apr 10 2014, 04:24) *
Как можно определить, с какими параметрами оптимизации задана копмиляция?
А какой у нас компилятор?
ViKo
Цитата(Сергей Борщ @ Apr 10 2014, 05:58) *
А какой у нас компилятор?

Кейловский, ARMCC из uVision.
Там из списка выбирается -O0... -O3, отдельно галка -Otime.
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 07:01) *
Не с той стороны подходите к решению задачи.
Чтобы не было зависимости от уровня оптимизации напишите асмовую вставку.
В моём примере она имеется в каком-то виде.
И будет вам счастьеsm.gif

Цитата(Shamil @ Apr 10 2014, 05:18) *
Надо бы, наверное, весь макрос поместить внутрь блока, чтобы вставлялся как один оператор
+1
Сергей Борщ
Цитата(ViKo @ Apr 10 2014, 05:01) *
Кейловский, ARMCC из uVision.
Я пас.

Цитата(demiurg_spb @ Apr 10 2014, 05:15) *
Чтобы не было зависимости от уровня оптимизации напишите асмовую вставку.
Угу. Вместо последнего цикла. А потом вспомнить про прерывания и забить на эту погрешность.
ViKo
Цитата
Чтобы не было зависимости от уровня оптимизации напишите асмовую вставку.

Взялся было... Но мне нужно загрузить переменную цикла в регистр. Где гарантия, это это регистр ничем не занят? laughing.gif

Цитата(Сергей Борщ @ Apr 10 2014, 06:24) *
Угу. Вместо последнего цикла. А потом вспомнить про прерывания и забить на эту погрешность.

Да, прерывания меня уже смутили, при проверке на больших длительностях. Но эта функция написана для коротких интервалов. Конкретно, для работы с двухстрочным ЖКИ. А для "серьезных" задержек у меня есть макро с таймером. И др.

Что-то "не лезет" asm, не принимает его компилятор! В чем дело?
Код
#define DELAY_CY(CY)    \
  if (CY >= 10)        \
    { __asm {"MOVS R1,#CY; LOOP: SUBS R1,R1,#1; BEQ LOOP;"} }

(407): error: #2901: Expected an inline assembly instruction
(407): error: #3081: expected end of line or a ";"
Никак.

Из ARM документа:
The inline assembler supports ARM assembly language only.
The embedded assembler can be used for Thumb and Thumb-2 support.

Отдельно писать?

Код
void DelAsm(uint32_t CY)
{
  uint32_t Reg;
__asm {
  MOVS Reg, #CY
  LOOP:
  SUBS Reg, Reg, #1
  BEQ LOOP
  }
}

yeah.gif
Сергей Борщ
Цитата(ViKo @ Apr 10 2014, 08:51) *
Взялся было... Но мне нужно загрузить переменную цикла в регистр. Где гарантия, это это регистр ничем не занят? laughing.gif
"Нормальный" инлайн-асм дает вам возможность сообщить компилятору, что вы испортили регистр. Или попросить компилятор выделить вам свободный в данный момент регистр.

Цитата(ViKo @ Apr 10 2014, 08:51) *
Но эта функция написана для коротких интервалов. Конкретно, для работы с двухстрочным ЖКИ.
Для ЖКИ паузы могут быть и длинее. Поставьте наибольшее значение, 6 циклов и забудьте. Разницу глазом заметить не успеете.

Цитата(ViKo @ Apr 10 2014, 08:51) *
Что-то "не лезет" asm, не принимает его компилятор! В чем дело?
Может ему кавычки не нравятся?

Цитата(ViKo @ Apr 10 2014, 08:51) *
Из ARM документа:
...
uint32_t Reg;
Вот - очень наглядно показано как попросить у компилятора регистр.
ViKo
Цитата(Сергей Борщ @ Apr 10 2014, 10:07) *
Вот - очень наглядно показано как попросить у компилятора регистр.

Это я сам... попросил. rolleyes.gif
В-общем, путь есть... пойду.
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 10:51) *
Из ARM документа:
The inline assembler supports ARM assembly language only.
The embedded assembler can be used for Thumb and Thumb-2 support.
Отдельно писать?
Для CM3 используется Thumb-2. Поэтому нет никакой возможности писать инлайн вставки.
Зато можно целиком процедуру:
Код
static __inline __asm uint32_t get_interrupt_state(void)
{
    mrs r0, primask
    bx lr
}

Цитата(ViKo @ Apr 10 2014, 10:51) *
Взялся было... Но мне нужно загрузить переменную цикла в регистр. Где гарантия, это это регистр ничем не занят? laughing.gif
http://infocenter.arm.com/help/topic/com.a...0042E_aapcs.pdf
пункт 5.1.1
ViKo
В-общем, не помогло.
Код
__inline void DelAsm(uint32_t CY)
{
__asm {
    LOOP:
    SUBS CY, CY, #1
    BNE LOOP
  }
}

Превращается при оптимизации -O0 в
Код
0000f0  2005              MOVS     r0,#5
0000f2  bf00              NOP      
0000f4  bf00              NOP      
                  |L1.246|
0000f6  1e40              SUBS     r0,r0,#1
0000f8  d000              BEQ      |L1.252|
0000fa  e7fc              B        |L1.246|
                  |L1.252|
0000fc  bf00              NOP

Компилятор чудит. И зачем?
P.S. Во всех остальных случаях компилируется, как надо.
Код
0000ca  2005              MOVS     r0,#5
                  |L1.204|
0000cc  1e40              SUBS     r0,r0,#1
0000ce  d1fd              BNE      |L1.204|



Цитата(demiurg_spb @ Apr 10 2014, 12:07) *
пункт 5.1.1

Вот отсюда ссылки нужные
http://www.keil.com/support/docs/3369.htm
MaxiMuz
А вот такая ситуация:
Пишу под Keil макрос OUTPIN_PP(port,nbit) который должен в случае , если nbit<8 выполнить
Код
do { GPIO##port->CRL&=~(GPIO_CRL_CNF##nbit); GPIO##port->CRL|=GPIO_CRL_MODE##nbit; } while (0);

а если иначе , то
Код
do { GPIO##port->CRH&=~(GPIO_CRH_CNF##nbit); GPIO##port->CRH|=GPIO_CRH_MODE##nbit; } while (0)

В идеальном варианте на этапе компиляции какимто образом выполнить проверку условия и вставить нужные операторы.
Вариант с
Код
#if nbit<8 OUTPIN_PP(port,nbit) do { GPIO##port->CRL&=~(GPIO_CRL_CNF##nbit); GPIO##port->CRL|=GPIO_CRL_MODE##nbit; } while (0);
#endif
не прокатывает
А с использованием программного вывода:
Код
#define _OUTPIN_PP(port,nbit)  if ((nbit)<8) {do { GPIO##port->CRL&=~(GPIO_CRL_CNF##nbit); GPIO##port->CRL|=GPIO_CRL_MODE##nbit; } while (0); }\
                                                             else do { GPIO##port->CRH&=~(GPIO_CRH_CNF##nbit); GPIO##port->CRH|=GPIO_CRH_MODE##nbit; } while (0)

выдает ошибку отсутствия предопределенного макроса.
Вариант с определением базового адреса и последующим вычислением адреса регистра известен
Код
#define PORTA   ((u32*)(APB2PERIPH_BASE + 0x0800))
#define PORTB   ((u32*)(APB2PERIPH_BASE + 0x0C00))
#define PORTC   ((u32*)(APB2PERIPH_BASE + 0x1000))
#define PORTD   ((u32*)(APB2PERIPH_BASE + 0x1400))
#define PORTE   ((u32*)(APB2PERIPH_BASE + 0x1800))
#define PORTF   ((u32*)(APB2PERIPH_BASE + 0x1C00))
#define PORTG   ((u32*)(APB2PERIPH_BASE + 0x2000))

#define pinOutPP_b(port,bit)  {*(port+((bit)/8))|=3UL<<(((bit)-(8*((bit)/8)))<<2); *(port+((bit)/8))&=~(3UL<<((((bit)-(8*((bit)/8)))<<2)+2));}
, хочу делать по другому .. собственно вопрос как
ViKo
Цитата(MaxiMuz @ Apr 10 2014, 12:16) *
#define pinOutPP_b(port,bit) {*(port+((bit)/8))|=3UL<<(((bit)-(8*((bit)/8)))<<2); *(port+((bit)/8))&=~(3UL<<((((bit)-(8*((bit)/8)))<<2)+2));}[/code], хочу делать по другому .. собственно вопрос как

Код
/*!****************************************************************************
@brief        Bit of Low/High byte Port configuration
@details    Конфигурация одиночного бита порта
@param        PORT - имя порта (A..G)
@param        BIT - номер бита (0..15)
@param        CM - конфигурация бита
@note        Используется перечисляемый тип CONF_MODE
@note        Для IN_PDPU нужный pull-down/pull-up задается в ODR
*/
#define GPIO_CONFB(PORT, BIT, CM)                    \
    *(uint32_t *)((uint32_t)(GPIO##PORT) + BIT / 8 * 4) =         \
    *(uint32_t *)((uint32_t)(GPIO##PORT) + BIT / 8 * 4)        \
        & (~(0xF << (BIT % 8) * 4)) | (CM << (BIT % 8) * 4)
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 13:14) *
Вот отсюда ссылки нужные
При чём тут нужные или нет?
Я вам ответил на вопрос о том какие регистры и как можно использовать, дав ссылку на первоисточник: AAPCS.
Зная, что кейл соответствует AAPCS.
Вот так никаких вариантов для компилятора изменить что-то при изменении уровня оптимизации нет.
Код
static __inline __asm void delay_8cycles(uint32_t x)
{
loop_delay_8cycles:
    nop
    nop
    nop
    nop
    subs r0,r0,#1
    bne  loop_delay_8cycles
    bx lr
}
Совет: придумывайте более сложные имена меткам, т.к. могут быть совпадения в одной единице трансляции и вылезет ошибка.
Незамеченной она конечно не останется, но зачем заранее закладывать мину?


Цитата(MaxiMuz @ Apr 10 2014, 13:16) *
А вот такая ситуация:
Очень неприлично влезать в чужой топик с вопросом совсем не по теме...
Хотите спросить - создайте тему со своим вопросом, или поищите по форуму. Ваш вопрос неоднократно обсуждался...
ViKo
Цитата(demiurg_spb @ Apr 10 2014, 12:35) *
При чём тут нужные или нет?
Я вам ответил на вопрос о том какие регистры и как можно использовать, дав ссылку на первоисточник: AAPCS.

Не о том был вопрос.
http://infocenter.arm.com/help/topic/com.a...9124245889.html
http://infocenter.arm.com/help/topic/com.a..._user_guide.pdf
7.14 Inline assembler and register access in C and C++ code

Насчет меток - пока не нашел, думаю, компилятор сам их различает в разных функциях.
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 13:49) *
Насчет меток - пока не нашел, думаю, компилятор сам их различает в разных функциях.
Ну думайте...
Я вам говорю о том, с чем лично УЖЕ сталкивался (не на самой последней версии компилятора).

ViKo
Цитата(demiurg_spb @ Apr 10 2014, 12:41) *
Вот так никаких вариантов для компилятора изменить что-то при изменении уровня оптимизации нет.

Вызываю функцию напрямую.
Код
  DelAsm(6);

__forceinline void DelAsm(uint32_t CY)
{
__asm {
    LOOP:
    SUBS CY, CY, #1
    BNE LOOP
  }
}

Получаю при -O0.
Код
0000f2  2006              MOVS     r0,#6
0000f4  bf00              NOP      
0000f6  bf00              NOP      
                  |L1.248|
0000f8  1e40              SUBS     r0,r0,#1
0000fa  d000              BEQ      |L1.254|
0000fc  e7fc              B        |L1.248|
                  |L1.254|
0000fe  bf00              NOP

А при -O1
Код
0000cc  2006              MOVS     r0,#6
                  |L1.206|
0000ce  1e40              SUBS     r0,r0,#1
0000d0  d1fd              BNE      |L1.206|
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 13:49) *
Не о том был вопрос.
Как не о том:
Цитата
Но мне нужно загрузить переменную цикла в регистр. Где гарантия, это это регистр ничем не занят?
Именно на этот вопрос и отвечает AAPCS, а не что-то другое. Ну да ладно. Главное результат...

Цитата(ViKo @ Apr 10 2014, 14:08) *
Вызываю функцию напрямую.
Вы мой пример попробуйте (из сообщения 22) - поймёте в чём разница.
Кстати, o0 - это совсем плохой вариант, не нужный НИКОГДА на моей практике.
Если вы увлекались ранее avr-gcc то могли бы увидеть такое в файле задержек delay.h:
Код
#ifndef __OPTIMIZE__
# warning "Compiler optimizations disabled; functions from <util/delay.h> won't work as designed"
#endif

А для вас возможным выходом из ситуации могло бы стать это решение:
http://www.keil.com/support/man/docs/ARMCC...EF_BCFJFGAA.htm
или это
http://www.keil.com/support/man/docs/ca/ca_optimize.htm
ViKo
Цитата(demiurg_spb @ Apr 10 2014, 13:13) *
Вы мой пример попробуйте - поймёте в чём разница.

Если в nop-ах - верю на слово.
Цитата
Кстати, o0 - это совсем плохой вариант, не нужный НИКОГДА на моей практике.

Я тоже не пользуюсь. Здесь вопрос, скорее, теоретический. Компилятор будто назло пихает ненужные команды. Для тех, кто не хочет платить? rolleyes.gif
Цитата
А для вас возможным выходом из ситуации могло бы стать это решение:
http://www.keil.com/support/man/docs/ARMCC...EF_BCFJFGAA.htm
или это
http://www.keil.com/support/man/docs/ca/ca_optimize.htm

Первое попробовал - работает! А эта прагма только на одну функцию будет действовать, или до конца файла? biggrin.gif
Вторая ссылка - для старого компилятора. И зачем их только там держат.

Ага, есть:
#pragma push
#pragma O3
function
#pragma pop
demiurg_spb
Цитата(ViKo @ Apr 10 2014, 14:54) *
Если в nop-ах - верю на слово.
Нет не в них, а в том что у меня __asm задан для всей функции, а у вас для вставки.
Цитата
Ага, есть:
#pragma push
#pragma O3
function
#pragma pop
Отлично!
Для изменения "упакованности" обходился двумя строчками прагмы:
Код
#pragma pack(push, 1)     // set 1 and save prev
...
#pragma pack(pop)          // restore prev
Не знаю можно ли это и тут применить...
ViKo
Сотворил.v2
Код
/*!****************************************************************************
@brief        Delay program loop
@param        NS - время задержки в ns
@note        Параметр округляется до ближайшего не меньшего, кратного тактам
@note        4 такта в цикле при любой оптимизации (принудительно -O3)
@note        Формула: D = 4 * CY4 - 1 + Nnop
*/
#define SYSCLK_MHZ 72
#define NS2CY(NS) ((NS * SYSCLK_MHZ + 999) / 1000)
#define DELAY_NS(NS) DELAY_CY(NS2CY(NS))

#define DELAY_CY(CY)                            \
  if      (CY == 1) { __NOP(); }                    \
  else if (CY == 2) { __NOP(); __NOP(); }                \
  else if (CY == 3) { __NOP(); __NOP(); __NOP(); }            \
  else {                                \
    if ((CY + 1) % 4 == 1) { __NOP(); }                    \
    if ((CY + 1) % 4 == 2) { __NOP(); __NOP(); }            \
    if ((CY + 1) % 4 == 3) { __NOP(); __NOP(); __NOP(); }        \
    DelayCY4((CY + 1) / 4); }                        \

/*!****************************************************************************
@brief        Delay 4 clocks in cycle
@param        CY4 - количество 4-тактовых циклов
@note        Delay = 4 * CY4 - 1
*/
#pragma push
#pragma O3
__forceinline void DelayCY4(uint32_t CY4)
{
__asm {
LOOP:
    SUBS CY4, CY4, #1
    BNE LOOP
  }
}
#pragma pop
Леонид Иванович
Цитата(ViKo @ Apr 10 2014, 09:51) *
Конкретно, для работы с двухстрочным ЖКИ.


А я для таких целей использую функции микросекундных задержек, которая пользуется таймером SysTick, не мешая ему выполнять свою основную функцию - генерирование системного интервала 1 мс:

Код
void TSysTimer::Delay_us(uint16_t d)
{
  uint32_t DelayStart = SysTick->VAL;
  uint32_t DelayTicks = d * CLK_PER_US;
  int32_t  Delta;
  do
  {
    Delta = DelayStart - SysTick->VAL;
    if(Delta < 0) Delta += CLK_PER_MS;
  }
  while(Delta < DelayTicks);
}

demiurg_spb
Цитата(Леонид Иванович @ Apr 13 2014, 23:24) *
А я для таких целей использую функции микросекундных задержек, которая пользуется таймером SysTick
DWT таймер более подходит для этой задачи. ИМХО.
ViKo
Цитата(Леонид Иванович @ Apr 13 2014, 22:24) *
А я для таких целей использую функции микросекундных задержек, которая пользуется таймером SysTick, не мешая ему выполнять свою основную функцию - генерирование системного интервала 1 мс

Годится, но только для интервалов короче 1 мс.
А для этого ЖКИ есть интервалы и по 30 мс. (Вообще, темный лепс, скачал несколько datasheet-ов на разные ЖКИ, есть такие странные процедуры установки режимов...)
toweroff
Цитата(ViKo @ Apr 14 2014, 08:38) *
Годится, но только для интервалов короче 1 мс.
А для этого ЖКИ есть интервалы и по 30 мс. (Вообще, темный лепс, скачал несколько datasheet-ов на разные ЖКИ, есть такие странные процедуры установки режимов...)

а у него нет никакого флага BUSY? чтобы не городить с задержками, а когда можно флаг опросить и дальше работать
ViKo
Цитата(toweroff @ Apr 14 2014, 08:01) *
а у него нет никакого флага BUSY? чтобы не городить с задержками, а когда можно флаг опросить и дальше работать

Есть, но чтобы его опросить (что и делаю), нужно прочитать байт, по всем правилам, с задержками. sm.gif
toweroff
Цитата(ViKo @ Apr 14 2014, 09:13) *
Есть, но чтобы его опросить (что и делаю), нужно прочитать байт, по всем правилам, с задержками. sm.gif

ок, можно отдать SysTick генерить все управляющие сигналы по стэйт-машине некой, а из него уже отсылать программный сигнал (или просто флаг) ждущей задаче
я так понимаю, этот таймер все равно по микросекундам генерится?
Сергей Борщ
Цитата(ViKo @ Apr 14 2014, 08:13) *
Есть, но чтобы его опросить (что и делаю), нужно прочитать байт, по всем правилам, с задержками. sm.gif
Да дайте вы удвоенную от максимальной задержку и забудьте о чтении. Оператор не заметит лишние 10 мс на перерисовку всего экрана, а программа упростится раза в три.
ViKo
Цитата(toweroff @ Apr 14 2014, 08:27) *
ок, можно отдать SysTick генерить все управляющие сигналы по стэйт-машине некой, а из него уже отсылать программный сигнал (или просто флаг) ждущей задаче
я так понимаю, этот таймер все равно по микросекундам генерится?

У меня задержка считается в ns. С точностью до такта микроконтроллера. SysTick Timer считает такты (или поделенные на 8) до 1-10 ms для RTOS (и у меня тоже есть), потом перезагружается. Поэтому более длинные интервалы не получить.
Можно большие задержки считать одним способом, а малые другим. А можно все одним. rolleyes.gif

Цитата(Сергей Борщ @ Apr 14 2014, 08:45) *
Да дайте вы удвоенную от максимальной задержку и забудьте о чтении. Оператор не заметит лишние 10 мс на перерисовку всего экрана, а программа упростится раза в три.

Так уж в три...? sm.gif Задержка занимает у меня от 3 до 6 ассемблерных команд. Куда уж проще? И выполняется именно столько времени, сколько мне нужно.
Там же все обращения к контроллеру ЖКИ идут ногодрыгом, без задержек - никак не обойтись.
А для выполнения ЖКИ команд нужны задержки побольше. Зачем мне терять время, если не проверять, когда они закончатся, а тупо ждать больше срока?
Сергей Борщ
Цитата(ViKo @ Apr 14 2014, 08:52) *
Так уж в три...? sm.gif Задержка занимает у меня от 3 до 6 ассемблерных команд. Куда уж проще?
Выкинуть чение Busy, дав задержки на выполнение команд дисплеем вдвое от максимальных в даташите (50мкс на все команды кроме очистки экрана, на нее 2мс или вообще не использовать). Уже только избавление он необходимости переключать порт на ввод и анализивровать Busy сводит все управление дисплеем к трем примитивнейшим функциям:
Код
void hd44780::write_tetrade(uint8_t tetrade)    // assume data in upper tetrade, lower tetrade is all zeros
{
    ON(LCD_EN);
    LCD_PORT = (LCD_PORT & 0x0F) | tetrade;
    _delay_us(1);
    OFF(LCD_EN);
    _delay_us(1);
}

void hd44780::write_data(uint8_t byte)
{
    write_tetrade(byte & 0xF0);
    write_tetrade(byte << 4);
    _delay_us(50);
    ON(LCD_RS);
}

void hd44780::write_command(uint8_t command)
{
    OFF(LCD_RS);
    write_data(command);
}
Сравните со своим ногодрыгом с чтением Busy.

Ну хорошо, еще инициализация:
Код
inline hd44780::hd44780()
{
    OFF(LCD_RS);
    _delay_ms(200);
    write_tetrade(3 << 4);      // set 8-bit mode
    _delay_ms(50);
    write_tetrade(3 << 4);      // set 8-bit mode again
    _delay_ms(50);
    write_tetrade(3 << 4);      // set 8-bit mode again, see http://electronix.ru/forum/index.php?s=&showtopic=19594&view=findpost&p=143374
    _delay_us(50);              // delay slightly longer than typical command execution time.
    write_tetrade(2 << 4);      // set 4-bit mode
    _delay_us(50);
    write_command(CURSOR_HOME);
    _delay_ms(2);
    write_command(0x06);        // increment cursor position during data write, disable display shift
    write_command(CURSOR_OFF);
}


Цитата(ViKo @ Apr 14 2014, 08:52) *
а тупо ждать больше срока?
А вы и так "тупо ждете больше срока" потому что в вашу задержку вклиниваются прерывания. Вывод на дисплей происходит так редко, что будет там задержка 50мкс или 20мкс на байт не заметит никто. Ну пусть у вас дисплей 4*40, пусть обновляете его целиком 4 раза в секунду (быстрее он не успеет отобразить, а оператор воспринять), ну потеряете вы лишних (50-20)*40*4*4=19200 мкс. Ах, 19мс каждую секунду ушло впустую, катастрофа. Зато сэкономлена неделя рабочего времени, за которую эти 19мс можно с большей пользой отыграть где-то в другом месте.
ViKo
Цитата(Сергей Борщ @ Apr 14 2014, 09:15) *
А вы и так "тупо ждете больше срока" потому что в вашу задержку вклиниваются прерывания.

Зато я могу посылать в ЖКИ символы, когда захочу. Совсем не обязательно "тупо ждать" задержку или флаг Busy, можно заняться чем-нибудь общественно-полезным между выводами, не опасаясь "уже можно посылать, или еще подождать?".

Нюанс всплыл - невозможно заинлайнить функцию (DelayCY4), находящуюся в другом файле. Пришлось в один объединить.
toweroff
Цитата(ViKo @ Apr 14 2014, 19:35) *
Нюанс всплыл - невозможно заинлайнить функцию (DelayCY4), находящуюся в другом файле. Пришлось в один объединить.

с месяц назад где-то здесь обсуждался вопрос вставки inline из другого файла, кстати

с задержками я делал вот так
Код
void HD44780_delay(int ns10)
{
volatile unsigned int trash_out;
volatile unsigned int trash_in;
int i;
    
    for (i=0; i<ns10; i++)
        trash_in = trash_out;
}

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

а с большими задержками - там и подождать сигнала от таймера не проблема, тем более, что это наверняка в отдельной задаче вертится
demiurg_spb
Цитата(ViKo @ Apr 14 2014, 19:35) *
Нюанс всплыл - невозможно заинлайнить функцию (DelayCY4), находящуюся в другом файле. Пришлось в один объединить.
Это не нюанс, это стандартное поведение.
Для решения подобных задач нужно всё в заголовочном файле размещать:
Код
static __inline void delay(int x)
{
    ....
}

Цитата(toweroff @ Apr 14 2014, 19:54) *
не панацея, конечно, но позволит уйти от асма, тем более что точная задержка как таковая не критична прям уж так
Не системный подход с кучей сорного кода, заслоняющего основную идею.
Здесь асм не столько для точности, а больше для снятия зависимости от уровня оптимизации.

Тут у меня самого возник вопрос, касательно задержек в gcc.
Вот такой код упорно приводит проект в нерабочее состояние:
Код
#define delay_4cycles(cy)                \
(__extension__({                         \
    uint32_t __x = (uint32_t)(cy);       \
    __asm__ __volatile__                 \
    (                                    \
        "loop%=: subs %[cnt],#1"  "\n\t" \
        "        bne  loop%="     "\n\t" \
        :                                \
        : [cnt]"r"(__x)                  \
        : "cc"                           \
    );                                   \
}))

for(;;)
{
    delay_4cycles(1);
    pin_toggle(LED_RED);
}
А именно не производится перезагрузка счётчика цикла внутри цикла for.
В данном примере 1 загружается в регистр единожды перед циклом for, что собственно является нежелательным поведением:
Код
8001df6:    f04f 0001     mov.w    r0, #1
...
8001e0a:    3801          subs    r0, #1
8001e0c:    d1fd          bne.n    8001e0a
8001e0e:    682b          ldr    r3, [r5, #0]
8001e10:    ea6f 0703     mvn.w    r7, r3
8001e14:    6037          str    r7, [r6, #0]
8001e16:    e7f8          b.n    8001e0a
А без использования асм всё нормально:
Код
8001e04:    bf00          nop
8001e06:    bf00          nop
8001e08:    bf00          nop
8001e0a:    bf00          nop
8001e0c:    6806          ldr    r6, [r0, #0]
8001e0e:    ea6f 0306     mvn.w    r3, r6
8001e12:    602b          str    r3, [r5, #0]
8001e14:    e7f6          b.n    8001e04

Прошу помощи у аудитории!

arm-none-eabi-gcc.EXE (GNU Tools for ARM Embedded Processors / bleeding-edge-toolchain-131005) 4.7.4 20130913 (prerelease)
CFLAGS += -flto
CFLAGS += -fomit-frame-pointer
CFLAGS += -falign-functions=16
CFLAGS += -fgraphite
CFLAGS += -funroll-loops
CFLAGS += -ffunction-sections
CFLAGS += -fdata-sections
CFLAGS += -O3
#CFLAGS += -O1
Сергей Борщ
Цитата(demiurg_spb @ Apr 15 2014, 09:18) *
Прошу помощи у аудитории!
cnt у вас меняется в асм-вставке, а компилятору вы об этом не сообщаете.
Попробуйте так, если в синтаксисе не ошибся:
Код
    __asm__ __volatile__                 \
    (                                    \
        "loop%=: subs %[cnt],#1"  "\n\t" \
        "        bne  loop%="     "\n\t" \
        : =[cnt]"r"(__x)                 \
        :                   \
        : "cc"                           \
    );                                   \

demiurg_spb
Цитата(Сергей Борщ @ Apr 15 2014, 10:59) *
cnt у вас меняется в асм-вставке, а компилятору вы об этом не сообщаете.
Согласно доке и не должен я об этом ничего сообщать.
Вы предлагаете использовать cnt как выходной write only регистр:
Код
#define delay_4cycles(cy)                \
(__extension__({                         \
    uint32_t __x = (uint32_t)(cy);       \
    __asm__ __volatile__                 \
    (                                    \
        "loop%=: subs %[cnt],#1"  "\n\t" \
        "        bne  loop%="     "\n\t" \
        : [cnt]"=r"(__x)                 \
        :                                \
        : "cc"                           \
    );                                   \
}))
что на мой взгляд несколько неверно, т.к. компилятор его вообще перестал инициализировать:
Код
8001e06:    3b01          subs    r3, #1
8001e08:    d1fd          bne.n    8001e06 <loop3411>
8001e0a:    6806          ldr    r6, [r0, #0]
8001e0c:    ea6f 0306     mvn.w    r3, r6
8001e10:    602b          str    r3, [r5, #0]
8001e12:    e7f8          b.n    8001e06 <loop3411>
На мой взгляд cnt - это input, а не output операнд (кстати =&r даёт такой же результат).
Сергей Борщ
Цитата(demiurg_spb @ Apr 15 2014, 10:15) *
Согласно доке и не должен я об этом ничего сообщать.
Здрасьте. А как же он узнает, что вы его испортили и его надо перегрузить?
Цитата(demiurg_spb @ Apr 15 2014, 10:15) *
Вы предлагаете использовать cnt как выходной write only регистр:
Нет, вы пропустили "=" перед [cnt]. Я его делаю и input и output, коим он фактически и является.

Сейчас. сверюсь со своими исходниками. Я допускал эту же ошибку, сейчас найду.

В первый раз не туда смотрел, извиняюсь.
Код
        : [cnt]"+r"(__x)                 \
demiurg_spb
Цитата(Сергей Борщ @ Apr 15 2014, 11:21) *
Нет, вы пропустили "=" перед [cnt]. Я его делаю и input и output, коим он фактически и является.
"=" перед [cnt] не даёт ставить - ошибка компиляции.
"=" перед r не то же самое?
Цитата(Сергей Борщ @ Apr 15 2014, 11:21) *
Код
        : [cnt]"+r"(__x)                 \
О! Оно!
Код
8001df6:    f04f 0001     mov.w    r0, #1
...
8001e0a:    4603          mov    r3, r0
8001e0c:    3b01          subs    r3, #1
8001e0e:    d1fd          bne.n    8001e0c
8001e10:    682f          ldr    r7, [r5, #0]
8001e12:    ea6f 0407     mvn.w    r4, r7
8001e16:    6034          str    r4, [r6, #0]
8001e18:    e7f7          b.n    8001e0a
Спасибо большое!!!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.