delayMicroseconds не корректно работает
- Войдите на сайт для отправки комментариев
Пнд, 19/02/2018 - 03:51
стоит задача эмулировать кодовую посылку неизвестного протокола. считал ее логическим анализатором - тайминги такие - 7000 | 13.7 | 4.5 | 6.7 | 3.2 | 7 | 3 | 1.7 | 8.2 | 7 | 3.2 | 1.7
пытаюсь воспроизвести на ардуино уно -
01 | PORTD |= (1 << 2); |
02 | delay(7); |
03 | PORTD &= ~(1 << 2); |
04 | delayMicroseconds(13.7); |
05 | PORTD |= (1 << 2); |
06 | delayMicroseconds(4.5); |
07 | PORTD &= ~(1 << 2); |
08 | delayMicroseconds(6.7); |
09 | PORTD |= (1 << 2); |
10 | delayMicroseconds(3.2); |
11 | PORTD &= ~(1 << 2); |
12 | delayMicroseconds(7); |
13 | PORTD |= (1 << 2); |
14 | delayMicroseconds(3); |
15 | PORTD &= ~(1 << 2); |
16 | delayMicroseconds(1.7); |
17 | PORTD |= (1 << 2); |
18 | delayMicroseconds(8.2); |
19 | PORTD &= ~(1 << 2); |
20 | delayMicroseconds(7); |
21 | PORTD |= (1 << 2); |
22 | delayMicroseconds(3.2); |
23 | PORTD &= ~(1 << 2); |
24 | delayMicroseconds(1.7); |
25 | PORTD |= (1 << 2); |
но получаю полную ахинею. что не так?

ахинея - это вот такая запись: delayMicroseconds(3.2);
Слабо сначала посмотреть в референсе тип параметра delayMicroseconds() ?
1
This function works very accurately
in
the range 3 microseconds and up.
unsigned int
т.е мне не задать менее 3 микросекунд? да и при записи целых чисел лучше не становится
1
This function works very accurately
in
the range 3 microseconds and up.
unsigned int
т.е мне не задать менее 3 микросекунд? да и при записи целых чисел лучше не становится
Вот как ты думаешь, только честно - если параметр функции беззнаковое двухбайтовое целое, то подставляя туда число с плавающей точкой - ты задавишь функцию авторитетом и она ВНЕЗАПНО начнёт работать с float? Нет дробных задержек в delayMicroseconds, только целые.
Что касается эмуляции конкретной посылки, то решение в лоб - написать код, который будет занимать столько тактов процессора, чтобы обеспечить нужные наносекундные задержки. Т.е. сколько-то NOP подряд, например. Один clock cycle на 16 МГц - это 62.5 наносекунды, емнип. Перемножай, дели - и получишь кол-во циклов, которое надо прогнать, чтобы получить требуемые тебе чёткие задержки.
да я же говорю - поокруглял все а она все равно как хочет так и работает
01
PORTD |= (1 << 2);
02
delay(7);
03
PORTD &= ~(1 << 2);
04
delayMicroseconds(14);
05
PORTD |= (1 << 2);
06
delayMicroseconds(5);
07
PORTD &= ~(1 << 2);
08
delayMicroseconds(7);
09
PORTD |= (1 << 2);
10
delayMicroseconds(3);
11
PORTD &= ~(1 << 2);
12
delayMicroseconds(7);
13
PORTD |= (1 << 2);
14
delayMicroseconds(3);
15
PORTD &= ~(1 << 2);
16
delayMicroseconds(2);
17
PORTD |= (1 << 2);
18
delayMicroseconds(8);
19
PORTD &= ~(1 << 2);
20
delayMicroseconds(7);
21
PORTD |= (1 << 2);
22
delayMicroseconds(3);
23
PORTD &= ~(1 << 2);
24
delayMicroseconds(2);
25
PORTD |= (1 << 2);
а можно пример такого кода а то я что то не пойму как это так
Помни, что
This function works very accurately in the range 3 microseconds and up. We cannot assure that delayMicroseconds will perform precisely for smaller delay-times.
а можно пример такого кода а то я что то не пойму как это так
Пример кода, как вставить нужное кол-во NOP? В гугль, видимо, сегодня только по продовольственным карточкам допуск...
1
__asm__ __volatile__ (
"nop\n\t"
);
Задержка 62.5 наносекунды. Дальше - сам, пожалуйста; думаю, с арифметикой справишься.
Собственно всё верно. 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(). Тупо, просто и очень понятно "сколько это".
а можно пример такого кода а то я что то не пойму как это так
Ну, например, так: http://arduino.ru/forum/proekty/analog-analogovogo-sintezatora#comment-261101
Помни, что
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 мкс выполняется с приемлемой точностью.
у меня получилось 20 asm("nop"); для задержки 1,5 микросекунды
но оно тоже работает как ему угодно. допустим 1 ноп дает задержку 250 наносекунд. 2 нопа продолжают давать такую же задержку и только 5 нопов дают задержку 500 наносекунд
но оно тоже работает как ему угодно. допустим 1 ноп дает задержку 250 наносекунд. 2 нопа продолжают давать такую же задержку и только 5 нопов дают задержку 500 наносекунд
а можно поинтересоваться. чем вы это измеряли?
логический анализатор показывает тайминги
логический анализатор показывает тайминги
выложите скетч - у вас между импульсами, возможно, попадают вызовы прерываний
01
void
setup
(
void
) {
02
DDRD |= (1 << 2);
03
}
04
05
void
loop
(
void
) {
06
PORTD |= (1 << 2);
07
delay(7);
08
PORTD &= ~(1 << 2);
09
delayMicroseconds(14);
10
PORTD |= (1 << 2);
11
delayMicroseconds(5);
12
PORTD &= ~(1 << 2);
13
delayMicroseconds(7);
14
PORTD |= (1 << 2);
15
delayMicroseconds(3);
16
PORTD &= ~(1 << 2);
17
delayMicroseconds(7);
18
PORTD |= (1 << 2);
19
delayMicroseconds(3);
20
PORTD &= ~(1 << 2);
21
delayMicroseconds(2);
22
PORTD |= (1 << 2);
23
delayMicroseconds(8);
24
PORTD &= ~(1 << 2);
25
delayMicroseconds(7);
26
PORTD |= (1 << 2);
27
delayMicroseconds(3);
28
PORTD &= ~(1 << 2);
29
delayMicroseconds(2);
30
PORTD |= (1 << 2);
31
}
не вижу кода с "nop"
В строке 7 замените delay на delayMicroseconds(7000);
А в setup вставьте
cli();
У Вас каждые 4 микросекунды происходит прерывание и начинает miilis считаться. Кстати, после правок, о которых я сказал, millis'у скажите до свидянья.
к сожалению ничего не изменяется. и даже asm("nop"); не дает точности - то 250 то 500 наносекунд. может подход у меня не верный в плане эмуляции? как то же ардуина общается с периферией - может надо все же реализовать протокол?
Так не может быть. Что-то Вы или не доделали , или не договариваете. Не может 1 nop давать задержку 250ns, и два столько же. Один даёт 62,5 нс, а два 125 - только что проверил.
дайте ваш код
Так не может быть. Что-то Вы или не доделали , или не договариваете. Не может 1 nop давать задержку 250ns, и два столько же. Один даёт 62,5 нс, а два 125 - только что проверил.
Женя! Он это в цикле делает! ;););)
тогда 4 такта: 1 на NOP, 1 - на инкремент счетчика, 1 на сравнение, 1 - на переход.
4 и есть - что равно 250 нс. ;) ;) ;)
=================
Евгений! Нужно прекращать возиться с новичками - "Кошка бросила котят, пусть е..ться как хотят!".
Новичек, он, как солдат - его куда ни целуй, везде жопа.
вообщем происходит какая то чертовщина. отказался я от первой затеи и сделал вот так -
01
void
setup
() {
02
DDRD |= (1 << 2);
03
}
04
05
void
loop
() {
06
PORTD &= ~(1 << 2); _delay_us(13);
07
PORTD |= (1 << 2);
08
SendBit(0b00101110);
09
PORTD |= (1 << 2); _delay_us(11);
10
SendBit(0b00000000);
11
12
13
14
delayMicroseconds(7000);
15
16
17
}
18
19
20
21
void
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 и ноп - все дает один и тот же результат
Блин деревянный. У тебя loop вызывается постоянно. Затраты на вызов функции сам посчитаешь, или как? NOP прекрасно работает, если есть прямые руки и светлая голова.
еще один, у кого "некорректно работает".... мозг.
Вот неужели сложно просто сказать что я делаю не так чем говорить загадками ещё больше путая? Все прям родились с ардуиной
Какие загадки? loop вызываетя постоянно, при этом накладные расходы - это вызов этой функции. Так понятней? Если хочешь генерить без накладных расходов, то:
01
void
loop
()
02
{
03
while
(1)
04
{
05
УРОВЕНЬ;
06
NOP;
// сколько надо по кол-ву
07
УРОВЕНЬ;
08
// и т.д.
09
}
10
}
Ещё прерывания запретить перед while(1) - и всё.
ничего не меняется. у меня сейчас проблема уже не с точностью задержек а с плавным уменьшением тайминга при отправке 00000000
Т.е. NOP уже ВНЕЗАПНО работает так, как и обязан, т.е. даёт нужную задержку в 62,5 наносекунды? Так и запишем.
да какой ноп - полный код выложен выше (23 сообщение). короче есть какой нибудь форум для особоодаренных? я туда пойду
как будто хер знает работу что ли у вас отбираю или деньги? сижу разобраться пытаюсь что в этом плохого?
Ооох. Про накладные расходы на вызовы функций/циклов ты пропустил мимо ушей? Тогда да - тебе на другой форум, например, на форум отоларингологов - там помогут разобраться, почему в уши ничего не входит.
Пойми - вычисления надо производить ДО критичных ко времени действий. Это как минимум. Либо - корректировать время задержки, в зависимости от времени, занимаемого вычислениями. Смекаешь?
Твою SendBit лучше переписать примерно так:
01
void
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
}
я реально походу тупой что ли? а как мне выполнять мой код без loop? то что вы привели в 27 сообщении тоже ведь содержит loop. и разницы я никакой не заметил -
01
void
setup
() {
02
DDRD |= (1 << 2);
03
cli();
04
}
05
06
void
loop
()
07
{
08
while
(1)
09
{
10
PORTD &= ~(1 << 2); _delay_us(13);
11
PORTD |= (1 << 2);
12
SendBit(0b00101110);
13
PORTD |= (1 << 2); _delay_us(11);
14
SendBit(0b00000000);
15
16
17
18
19
delayMicroseconds(7000);
20
}
21
}
Разницы не заметил, но она есть, поверь. В моём случае в loop заходит только однажды и далее в нём и живёт. В твоём - каждый раз туда заходит, более того, если ты посмотришь исходники - ты увидишь, что после loop там ещё вызываются всякие serialEvent - тоже трата времени.
По поводу SendBit я выше отредактировал сообщение.
я реально походу тупой что ли? а как мне выполнять мой код без loop? то что вы привели в 27 сообщении тоже ведь содержит loop. и разницы я никакой не заметил -
Ежели внутри loop(). как и любой другой функции, впрочем, есть бесконечный цикл, то постоянных входов в нее и выходов из нее не осуществляется, т.е. накладные расходы на эти операции отсутствуют. Получается вот такое:
1
start:
2
pinOn;
3
wait;
4
pinOff;
5
goto
start:
Так понятней?
iopq - в функции SendBit каждый шаг For - инкремент i. сравнение If - все знамает время и все задержки нужно корректировать с учетом этого. Если вы отсылаете 1-2 байта. можно вообще отказался от функции и for и записать код линейно
я реально походу тупой что ли?
Да, не тупой, просто опыта нет и читаете невнимательно.
Ну, давайте я попробую объяснить.
Если задержкий у Вас достаточно большие (миллисекунды, скажем), то можно плевать на накладные расходы. Если э задержки маленьки, то плевать нельзя, из надо учитывать.
И вот теперь смотрите. Я возьму код, похожий на Ваш, пока без while:
01
PORTD &= ~(1 << 2);
02
_NOP();
03
_NOP();
04
_NOP();
05
PORTD |= (1 << 2);
06
_NOP();
07
_NOP();
08
PORTD |= (1 << 2);
09
_NOP();
10
PORTD |= (1 << 2);
При любых расчётах Вы должны понимать, что строки 1, 5, 8 и 10 тоже выполняются не мгновенно и это время надо учитывать.
Этот код странслируется в такие машинные команды:
01
cbi 0xb,2
02
nop
03
nop
04
nop
05
sbi 0xb,2
06
nop
07
nop
08
sbi 0xb,2
09
nop
10
sbi 0xb,2
Длительность исполнения nop - 1 такт (62,5 нс).
Длительность исполнения команд cbi и sbi - 2 такта (125 нс)
Таким образом, весь приведённый кусок кода исполнится за 14 тактов, т.е. за 875 наносекунд.
Вот так и надо считать, каждую команду. Тогда Вы точно получите то, что хотите.
Если Вы добавите while или ещё чего - это всё команды. Их тоже надо также выписать и считать.
Стало понятнее?
длинные последовательности NOP удаляет оптимизатор при стандартной настройке "-Os" ;) ;) ;)
так что учите новичка #PRAGMA GCC optimize ("-O0") или правильным ассемблерным вставкам с волатилью... ;)
------------------------
его первый код можно вот так переделать, тогда работает при неизмененной ИДЕ.
01
#define Toggle4() (PIND |= 1<<4)
02
03
void
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
11
void
setup
() {
12
pinMode (4,OUTPUT);
13
14
}
15
16
17
void
loop
() {
18
19
PORTD |= (1 << 4);
20
delay(7);
21
noInterrupts();
22
23
24
Toggle4();
25
my_delay(13.7);
26
Toggle4();
27
my_delay(4.5);
28
Toggle4();
29
my_delay(6.7);
30
Toggle4();
31
my_delay(3.2);
32
Toggle4();
33
my_delay(7);
34
Toggle4();
35
my_delay(3);
36
Toggle4();
37
my_delay(1.7);
38
Toggle4();
39
my_delay(8.2);
40
Toggle4();
41
my_delay(7);
42
Toggle4();
43
my_delay(3.2);
44
Toggle4();
45
my_delay(1.7);
46
Toggle4();
47
48
interrupts();
49
}
вот такая картинка получится... у меня как раз полчаса свободные были...
правильным ассемблерным вставкам с волатилью... ;)
Так вроде ж _NOP();- как раз такая хрень и есть (хотя лезть смотреть лень)
Вы только поясните человеку, что Ваш код нормально сможет работать только и исключительно только с константными параметрами в вызовах my_delay(), а то он туда как забабахает float переменную.. )
DIYMan спасибо с вашим кодом отправки мне удалось подогнать тайминг под образец. пока что на мою посылку устройство не отвечает. разбираюсь
"...Раз пошла такая пьянка", то вот вам самое правильное решение подобной задачи:
Пользуем SPI - каждый байт - это бит нашего кода. Выход на MOSI, ессно.
01
#include <SPI.h>
02
03
void
setup
() {
04
05
SPI.begin();
06
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
07
}
08
09
10
const
byte
CODE = 0b11011001;
11
12
void
loop
() {
13
14
15
16
for
(
byte
i = 0b10000000; i; i >>= 1)
17
if
(CODE & i) SPI.transfer(0b00111111);
18
else
SPI.transfer(0b00000001);
19
20
delayMicroseconds(20);
21
}
Ну и картинка с логаналайзера.
не плохой код. как увеличить тайминг с 1,5 мкс до 3 мкс? и не могу понять почему на оригинальный запрос устройство отвечает а на мой не хочет -
я понимаю что это не UART. это для наглядности
не плохой код. как увеличить тайминг с 1,5 мкс до 3 мкс? и не могу понять почему на оригинальный запрос устройство отвечает а на мой не хочет -
1. сними всю посылку логаналайзером и выложи в каком-нибудь читаемом виде. Если у тебя стандартный китаец за 300руб (типа Saleaelogic), то в программе есть экспорт в CSV.
2. как увеличить?: подключай ардуино, логаналайзер и комп - и подбирай скорость SPI и коды для передачи "1" и "0".
...Я что-то начал подозревать, что ты все это не в железе, а в "протеусе" делашь... если так, то этот онанизм - без меня.
3. устройство твое может не принимать из-за неправильного "обрамления" передачи... как вариант бывает нужно сперва выдать некий условный "ресет" - это может быть высокий( или низкий ;) ) уровень аж на 500мс. Поэтому и говорю - сними тайминг аккуратно.
никакого протеуса - все в чистейшем железе. http://my-files.ru/x2xniu ссылка на лог
никакого протеуса - все в чистейшем железе. http://my-files.ru/x2xniu ссылка на лог
Это или неудачная шутка или издевательство. 500К лога, да? Две с чем-то секунды... круто!
сделай его читаемым и в таблице пометь моменты начала и конца кода, на который реагирует твой прибор.
Просто открой этот CSV в ехеле или опенофисе и пометь все, что нужно. тогда можно подбирать.
я могу сделать файл поболее только для чего если все что происходит занимает даже меньше времени просто я не могу быстрее руками и кнопку в программе нажать и питание подать и еще раз нажать. что то я не пойму с этим файлом - там же ничего не понятно - нули и единицы и время
наверное я что то недопонял. у меня полученный файл выглядит вот так- тут же месяц сидеть надо что бы проставить что у меня происходит
что то я не пойму с этим файлом - там же ничего не понятно - нули и единицы и время
ты забавный! Ты его в Exel открой...
Я тебе написал, что он слишком большой для анализа, нахрена нужно целых 2 секунды данных?
1. вот твой причесаанный файл. (первая 1000 строк)
Уже все не так, как ты написал, все "0" имеют длительность округленно 1 мкс.
http://my-files.ru/kmbig4 не знаю так или не так