delayMicroseconds не корректно работает

iopq
Offline
Зарегистрирован: 05.07.2016

стоит задача эмулировать кодовую посылку неизвестного протокола. считал ее логическим анализатором - тайминги такие - 7000 | 13.7 | 4.5 | 6.7 | 3.2 | 7 | 3 | 1.7 | 8.2 | 7 | 3.2 | 1.7 

пытаюсь воспроизвести на ардуино уно -

01PORTD |= (1 << 2);
02delay(7);
03PORTD &= ~(1 << 2);
04delayMicroseconds(13.7);
05PORTD |= (1 << 2);
06delayMicroseconds(4.5);
07PORTD &= ~(1 << 2);
08delayMicroseconds(6.7);
09PORTD |= (1 << 2);
10delayMicroseconds(3.2);
11PORTD &= ~(1 << 2);
12delayMicroseconds(7);
13PORTD |= (1 << 2);
14delayMicroseconds(3);
15PORTD &= ~(1 << 2);
16delayMicroseconds(1.7);
17PORTD |= (1 << 2);
18delayMicroseconds(8.2);
19PORTD &= ~(1 << 2);
20delayMicroseconds(7);
21PORTD |= (1 << 2);
22delayMicroseconds(3.2);
23PORTD &= ~(1 << 2);
24delayMicroseconds(1.7);
25PORTD |= (1 << 2);

 

но получаю полную ахинею. что не так?
b707
Offline
Зарегистрирован: 26.05.2017

ахинея - это вот такая запись:  delayMicroseconds(3.2);

Слабо сначала посмотреть в референсе тип параметра delayMicroseconds() ?

iopq
Offline
Зарегистрирован: 05.07.2016
1This function works very accurately in the range 3 microseconds and up.

unsigned int

 т.е мне не задать менее 3 микросекунд? да и при записи целых чисел лучше не становится

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

iopq пишет:

1This function works very accurately in the range 3 microseconds and up.

unsigned int

 т.е мне не задать менее 3 микросекунд? да и при записи целых чисел лучше не становится

Вот как ты думаешь, только честно - если параметр функции беззнаковое двухбайтовое целое, то подставляя туда число с плавающей точкой - ты задавишь функцию авторитетом и она ВНЕЗАПНО начнёт работать с float? Нет дробных задержек в delayMicroseconds, только целые.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Что касается эмуляции конкретной посылки, то решение в лоб - написать код, который будет занимать столько тактов процессора, чтобы обеспечить нужные наносекундные задержки. Т.е. сколько-то NOP подряд, например. Один clock cycle на 16 МГц - это 62.5 наносекунды, емнип. Перемножай, дели - и получишь кол-во циклов, которое надо прогнать, чтобы получить требуемые тебе чёткие задержки.

iopq
Offline
Зарегистрирован: 05.07.2016

да я же говорю - поокруглял все а она все равно как хочет так и работает

01PORTD |= (1 << 2);
02delay(7);
03PORTD &= ~(1 << 2);
04delayMicroseconds(14);
05PORTD |= (1 << 2);
06delayMicroseconds(5);
07PORTD &= ~(1 << 2);
08delayMicroseconds(7);
09PORTD |= (1 << 2);
10delayMicroseconds(3);
11PORTD &= ~(1 << 2);
12delayMicroseconds(7);
13PORTD |= (1 << 2);
14delayMicroseconds(3);
15PORTD &= ~(1 << 2);
16delayMicroseconds(2);
17PORTD |= (1 << 2);
18delayMicroseconds(8);
19PORTD &= ~(1 << 2);
20delayMicroseconds(7);
21PORTD |= (1 << 2);
22delayMicroseconds(3);
23PORTD &= ~(1 << 2);
24delayMicroseconds(2);
25PORTD |= (1 << 2);

 

iopq
Offline
Зарегистрирован: 05.07.2016

а можно пример такого кода а то я что то не пойму как это так

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Помни, что 

This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

iopq пишет:

а можно пример такого кода а то я что то не пойму как это так

Пример кода, как вставить нужное кол-во NOP? В гугль, видимо, сегодня только по продовольственным карточкам допуск...

1__asm__ __volatile__ ("nop\n\t");

Задержка 62.5 наносекунды. Дальше - сам, пожалуйста; думаю, с арифметикой справишься.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Собственно всё верно. delayMicroseconds() плохо работает с задержками менее 3мксек, потому как время задержки в ней вычисляется БЕЗ учета вызова и возврата в функцию. А это 5 и 5 тактовой. итого 625нсек или 0.625мксек.

Если нужно точнее, то как Вам уже написали - ассемблерная вставка в тело нужного количества nop или использовать макрос типа _delay_loop_1() из utils/delay_basic.h или _delay_loop_2(), смотря что Вам больше подходит. Первый втыкает задержки из 8-и байтовой переменной по 3 такта, а второй из 16-байтовой по 4 такта. Давным-давно уже выносил их себе в arhat.h как макросы delay8() и delay16(). Тупо, просто и очень понятно "сколько это".

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

iopq пишет:

а можно пример такого кода а то я что то не пойму как это так

Ну, например, так: http://arduino.ru/forum/proekty/analog-analogovogo-sintezatora#comment-261101

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

DetSimen пишет:

Помни, что 

This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.

ПРактика показывает, что на 16-МГц контроллерах уже 1 мкс выполняется с приемлемой точностью.

iopq
Offline
Зарегистрирован: 05.07.2016

у меня получилось 20 asm("nop"); для задержки 1,5 микросекунды

но оно тоже работает как ему угодно. допустим 1 ноп дает задержку 250 наносекунд. 2 нопа продолжают давать такую же задержку и только 5 нопов дают задержку 500 наносекунд

b707
Offline
Зарегистрирован: 26.05.2017

iopq пишет:

но оно тоже работает как ему угодно. допустим 1 ноп дает задержку 250 наносекунд. 2 нопа продолжают давать такую же задержку и только 5 нопов дают задержку 500 наносекунд

а можно поинтересоваться. чем вы это измеряли?

iopq
Offline
Зарегистрирован: 05.07.2016

логический анализатор показывает тайминги

b707
Offline
Зарегистрирован: 26.05.2017

iopq пишет:

логический анализатор показывает тайминги

выложите скетч - у вас между импульсами, возможно, попадают вызовы прерываний

iopq
Offline
Зарегистрирован: 05.07.2016
01void setup(void) {
02  DDRD |= (1 << 2);
03}
04 
05void loop(void) {
06PORTD |= (1 << 2);
07delay(7);
08PORTD &= ~(1 << 2);
09delayMicroseconds(14);
10PORTD |= (1 << 2);
11delayMicroseconds(5);
12PORTD &= ~(1 << 2);
13delayMicroseconds(7);
14PORTD |= (1 << 2);
15delayMicroseconds(3);
16PORTD &= ~(1 << 2);
17delayMicroseconds(7);
18PORTD |= (1 << 2);
19delayMicroseconds(3);
20PORTD &= ~(1 << 2);
21delayMicroseconds(2);
22PORTD |= (1 << 2);
23delayMicroseconds(8);
24PORTD &= ~(1 << 2);
25delayMicroseconds(7);
26PORTD |= (1 << 2);
27delayMicroseconds(3);
28PORTD &= ~(1 << 2);
29delayMicroseconds(2);
30PORTD |= (1 << 2);
31}
b707
Offline
Зарегистрирован: 26.05.2017

не вижу кода с "nop"

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

В строке 7 замените delay на delayMicroseconds(7000);

А в setup вставьте 

cli();

У Вас каждые 4 микросекунды происходит прерывание и начинает miilis считаться. Кстати, после правок, о которых я сказал, millis'у скажите до свидянья.

iopq
Offline
Зарегистрирован: 05.07.2016

к сожалению ничего не изменяется. и даже asm("nop"); не дает точности - то 250 то 500 наносекунд. может подход у меня не верный в плане эмуляции? как то же ардуина общается с периферией - может надо все же реализовать протокол? 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так не может быть. Что-то Вы или не доделали , или не договариваете. Не может 1 nop давать задержку 250ns, и два столько же. Один даёт 62,5 нс, а два 125 - только что проверил. 

iopq
Offline
Зарегистрирован: 05.07.2016

дайте ваш код

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

Так не может быть. Что-то Вы или не доделали , или не договариваете. Не может 1 nop давать задержку 250ns, и два столько же. Один даёт 62,5 нс, а два 125 - только что проверил. 

Женя! Он это в цикле делает! ;););)

тогда 4 такта: 1 на NOP, 1 - на инкремент счетчика, 1 на сравнение, 1 - на переход.

4 и есть - что равно 250 нс. ;) ;) ;)

=================

Евгений! Нужно прекращать возиться с новичками - "Кошка бросила котят, пусть е..ться как хотят!".

Новичек, он, как солдат - его куда ни целуй, везде жопа.

iopq
Offline
Зарегистрирован: 05.07.2016

вообщем происходит какая то чертовщина. отказался я от первой затеи и сделал вот так -

01void setup() {
02DDRD |= (1 << 2);
03}
04 
05void loop() {
06PORTD &= ~(1 << 2); _delay_us(13);
07PORTD |= (1 << 2);
08SendBit(0b00101110);
09PORTD |= (1 << 2); _delay_us(11);  
10SendBit(0b00000000);
11  
12    
13 
14delayMicroseconds(7000);
15 
16 
17  }
18 
19 
20 
21void SendBit(long Code)
22{    
23  for (byte i=8;i>0;i--){
24   if (bitRead(Code, i-1) & 1){
25      PORTD &= ~(1 << 2);
26      _delay_us(1);
27      PORTD |= (1 << 2);
28      _delay_us(6);
29    } else {
30      PORTD &= ~(1 << 2);
31      _delay_us(6);
32      PORTD |= (1 << 2);
33      _delay_us(1);
34    }
35}
36}

при отправке SendBit(0b00000000); с этими нулями происходит невероятное - тайминг первого нуля = 4,5 микросекунды и далее все уменьшается и уменьшается и на последнем нуле уже = 1,750 микросекунды т.е более чем в два раза уменьшается. при чем если отправлять только нули - все замечательно. я уже пробовал по всякому- и прерывания отключал, и delayMicroseconds и  _delay_us и ноп - все дает один и тот же результат

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Блин деревянный. У тебя loop вызывается постоянно. Затраты на вызов функции сам посчитаешь, или как? NOP прекрасно работает, если есть прямые руки и светлая голова.

b707
Offline
Зарегистрирован: 26.05.2017

еще один, у кого "некорректно работает".... мозг.

iopq
Offline
Зарегистрирован: 05.07.2016

Вот неужели сложно просто сказать что я делаю не так чем говорить загадками ещё больше путая? Все прям родились с ардуиной

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Какие загадки? loop вызываетя постоянно, при этом накладные расходы - это вызов этой функции. Так понятней? Если хочешь генерить без накладных расходов, то:

01void loop()
02{
03 while(1)
04 {
05   УРОВЕНЬ;
06   NOP; // сколько надо по кол-ву
07   УРОВЕНЬ;
08   // и т.д.
09 }
10}

Ещё прерывания запретить перед while(1) - и всё.

iopq
Offline
Зарегистрирован: 05.07.2016

ничего не меняется. у меня сейчас проблема уже не с точностью задержек а с плавным уменьшением тайминга при отправке 00000000

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Т.е. NOP уже ВНЕЗАПНО работает так, как и обязан, т.е. даёт нужную задержку в 62,5 наносекунды? Так и запишем.

iopq
Offline
Зарегистрирован: 05.07.2016

да какой ноп - полный код выложен выше (23 сообщение). короче есть какой нибудь форум для особоодаренных? я туда пойду

iopq
Offline
Зарегистрирован: 05.07.2016

как будто хер знает работу что ли у вас отбираю или деньги? сижу разобраться пытаюсь что в этом плохого?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Ооох. Про накладные расходы на вызовы функций/циклов ты пропустил мимо ушей? Тогда да - тебе на другой форум, например, на форум отоларингологов - там помогут разобраться, почему в уши ничего не входит.

Пойми - вычисления надо производить ДО критичных ко времени действий. Это как минимум. Либо - корректировать время задержки, в зависимости от времени, занимаемого вычислениями. Смекаешь?

 

Твою SendBit  лучше переписать примерно так:

01void SendBit(byte Code)
02{
03  byte mask = 0x80;
04  for (byte i=8;i>0;i--)
05  {
06 
07   if (Code & mask)
08   {
09      PORTD &= 0xFB;
10      _delay_us(1);
11      PORTD |= 4;
12      _delay_us(6);
13    }
14    else
15    {
16      PORTD &= 0xFB;
17      _delay_us(6);
18      PORTD |= 4;
19      _delay_us(1);
20    }
21     
22    mask >>= 1;
23  }
24}

 

iopq
Offline
Зарегистрирован: 05.07.2016

я реально походу тупой что ли? а как мне выполнять мой код без loop? то что вы привели в 27 сообщении тоже ведь содержит loop. и разницы я никакой не заметил -

01void setup() {
02DDRD |= (1 << 2);
03cli();
04}
05 
06void loop()
07{
08 while(1)
09 {
10PORTD &= ~(1 << 2); _delay_us(13);
11PORTD |= (1 << 2);
12SendBit(0b00101110);
13PORTD |= (1 << 2); _delay_us(11);  
14SendBit(0b00000000);
15 
16  
17    
18 
19delayMicroseconds(7000);
20 }
21}

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

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

По поводу SendBit я выше отредактировал сообщение.

sadman41
Offline
Зарегистрирован: 19.10.2016

iopq пишет:

я реально походу тупой что ли? а как мне выполнять мой код без loop? то что вы привели в 27 сообщении тоже ведь содержит loop. и разницы я никакой не заметил -

Ежели внутри loop(). как и любой другой функции, впрочем, есть бесконечный цикл, то постоянных входов в нее и выходов из нее не осуществляется, т.е. накладные расходы на эти операции отсутствуют.  Получается вот такое:

1start:
2   pinOn;
3   wait;
4   pinOff;
5goto start:

Так понятней?

 

b707
Offline
Зарегистрирован: 26.05.2017

iopq - в функции SendBit каждый шаг For - инкремент i. сравнение If - все знамает время и все задержки нужно корректировать с учетом этого. Если вы отсылаете 1-2 байта. можно вообще отказался от функции и for и записать код линейно

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

iopq пишет:

я реально походу тупой что ли? 

Да, не тупой, просто опыта нет и читаете невнимательно.

Ну, давайте я попробую объяснить.

Если задержкий у Вас достаточно большие (миллисекунды, скажем), то можно плевать на накладные расходы. Если э задержки маленьки, то плевать нельзя, из надо учитывать.

И вот теперь смотрите. Я возьму код, похожий на Ваш, пока без while:

01PORTD &= ~(1 << 2);
02_NOP();
03_NOP();
04_NOP();
05PORTD |= (1 << 2);
06_NOP();
07_NOP();
08PORTD |= (1 << 2);
09_NOP();
10PORTD |= (1 << 2);

При любых расчётах Вы должны понимать, что строки 1, 5, 8 и 10 тоже выполняются не мгновенно и это время надо учитывать.

Этот код странслируется в такие машинные команды:

01cbi 0xb,2
02nop
03nop
04nop
05sbi 0xb,2
06nop
07nop
08sbi 0xb,2
09nop
10sbi 0xb,2

Длительность исполнения nop - 1 такт (62,5 нс).
Длительность исполнения команд cbi и sbi - 2 такта (125 нс)

Таким образом, весь приведённый кусок кода исполнится за 14 тактов, т.е. за 875 наносекунд.

Вот так и надо считать, каждую команду. Тогда Вы точно получите то, что хотите.

Если Вы добавите while или ещё чего - это всё команды. Их тоже надо также выписать и считать.

Стало понятнее?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

длинные последовательности NOP удаляет оптимизатор при стандартной настройке "-Os" ;) ;) ;)

так что учите новичка #PRAGMA GCC optimize ("-O0") или правильным ассемблерным вставкам с волатилью... ;)

------------------------

его первый код можно вот так переделать, тогда работает при неизмененной ИДЕ.

01#define Toggle4()   (PIND |= 1<<4)
02 
03void  asm_delay(const uint8_t d){
04  uint8_t dd = d;
05  do _NOP(); while(dd--);
06}
07 
08 
09#define my_delay(dt)  asm_delay((dt*4.0) - 3 );
10 
11void setup() {
12  pinMode (4,OUTPUT);
13 
14}
15 
16 
17void loop() {
18 
19PORTD |= (1 << 4);
20delay(7);
21noInterrupts();
22 
23 
24Toggle4();
25my_delay(13.7);
26Toggle4();
27my_delay(4.5);
28Toggle4();
29my_delay(6.7);
30Toggle4();
31my_delay(3.2);
32Toggle4();
33my_delay(7);
34Toggle4();
35my_delay(3);
36Toggle4();
37my_delay(1.7);
38Toggle4();
39my_delay(8.2);
40Toggle4();
41my_delay(7);
42Toggle4();
43my_delay(3.2);
44Toggle4();
45my_delay(1.7);
46Toggle4();
47 
48interrupts();
49}

вот такая картинка получится... у меня как раз полчаса свободные были...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

правильным ассемблерным вставкам с волатилью... ;)

Так вроде ж _NOP();- как раз такая хрень и есть (хотя лезть смотреть лень)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вы только поясните человеку, что Ваш код нормально сможет работать только и исключительно только с константными параметрами в вызовах my_delay(), а то он туда как забабахает float переменную.. )

iopq
Offline
Зарегистрирован: 05.07.2016

DIYMan спасибо с вашим кодом отправки мне удалось подогнать тайминг под образец. пока что на мою посылку устройство не отвечает. разбираюсь

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

"...Раз пошла такая пьянка", то вот вам самое правильное решение подобной задачи:

Пользуем SPI - каждый байт - это бит нашего кода. Выход на MOSI, ессно.

01#include <SPI.h>
02 
03void setup() {
04   
05SPI.begin();
06SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
07}
08 
09 
10const byte CODE = 0b11011001;
11 
12void loop() {
13   
14 
15 
16for(byte i = 0b10000000; i; i >>= 1)   
17    if (CODE & i) SPI.transfer(0b00111111);
18    else          SPI.transfer(0b00000001);
19 
20delayMicroseconds(20);
21}

Ну и картинка с логаналайзера.

iopq
Offline
Зарегистрирован: 05.07.2016

не плохой код. как увеличить тайминг с 1,5 мкс до 3 мкс? и не могу понять почему на оригинальный запрос устройство отвечает а на мой не хочет -

я понимаю что это не UART. это для наглядности

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

iopq пишет:

не плохой код. как увеличить тайминг с 1,5 мкс до 3 мкс? и не могу понять почему на оригинальный запрос устройство отвечает а на мой не хочет -

1. сними всю посылку логаналайзером и выложи в каком-нибудь читаемом виде. Если у тебя стандартный китаец за 300руб (типа Saleaelogic), то в программе есть экспорт в CSV.

2. как увеличить?: подключай ардуино, логаналайзер и комп - и подбирай скорость SPI и коды для передачи "1" и "0".

...Я что-то начал подозревать, что ты все это не в железе, а в "протеусе" делашь...  если так, то этот онанизм - без меня.

3. устройство твое может не принимать из-за неправильного "обрамления" передачи... как вариант бывает нужно сперва выдать некий условный "ресет" - это может быть высокий( или низкий ;) ) уровень аж на 500мс. Поэтому и говорю - сними тайминг аккуратно.

iopq
Offline
Зарегистрирован: 05.07.2016

никакого протеуса - все в чистейшем железе. http://my-files.ru/x2xniu ссылка на лог

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

iopq пишет:

никакого протеуса - все в чистейшем железе. http://my-files.ru/x2xniu ссылка на лог

Это или неудачная шутка или издевательство. 500К лога, да? Две с чем-то секунды... круто!

сделай его читаемым и в таблице пометь моменты начала и конца кода, на который реагирует твой прибор.

Просто открой этот CSV в ехеле или опенофисе и пометь все, что нужно. тогда можно подбирать.

iopq
Offline
Зарегистрирован: 05.07.2016

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

iopq
Offline
Зарегистрирован: 05.07.2016

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

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

iopq пишет:

 что то я не пойму с этим файлом - там же ничего не понятно - нули и единицы и время

ты забавный! Ты его в Exel открой...

Я тебе написал, что он слишком большой для анализа, нахрена нужно целых 2 секунды данных?

1. вот твой  причесаанный  файл.    (первая 1000 строк)

Уже все не так, как ты написал, все "0" имеют длительность округленно 1 мкс.

 

iopq
Offline
Зарегистрирован: 05.07.2016

http://my-files.ru/kmbig4 не знаю так или не так