Продолжаем работу с сопроцессором ULP и также мы продолжаем работу с датчиком температуры DS18B20, взаимодействующим с контроллером по шине 1-Wire. На данном уроке мы сравним контрольную сумму, принятую из шины с рассчитанной. Тем самым мы убедимся в целостности принятых данных из скратчпада датчика.
Схема наша осталась та же, что и в прошлом уроке — отладочная плата с контроллером esp32, подключенный к ней датчик температуры DS18B20, а также логический анализатор, подключенный к сигнальной ножке датчика для анализа обмена данными по шине
Проект за основу был взят из прошлого урока с именем ULP_FSM_ONEWIRE_READ и получил новое имя ULP_FSM_DS18B20_CRC.
Откроем проект в Espressif IDE и в функции app_main файл main.c добавим приведение типа вот здесь
printf("Data R0: 0x%04x\n", (unsigned int) ulp_dataR0 & UINT16_MAX);
Дело в том, что тем, кто обновил до последней версии Espressif IDE и комплект IDF, придётся встретиться с ругательством на несоответствие типов.
Далее идём в файл ulp_assembly_source_file.S и ниже процедуры send_bit добавим процедуру, которая будет заниматься контрольной суммой
1 2 |
checkCRC8_bytes: ret |
Отталкиваться будем от примерного алгоритм расчета контрольной суммы, код которой запишем в закомментированном виде в нашей процедуре
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
checkCRC8_bytes: /* char CRC8(const char *data,int length) { char crc = 0x00; char extract; char sum; for(int i=0;i<length;i++) { extract = *data; for (char tempI = 8; tempI; tempI--) { sum = (crc ^ extract) & 0x01; crc >>= 1; if (sum) crc ^= 0x8C; extract >>= 1; } data++; } return crc; } */ |
Инициализируем один регистр для набора значения контрольной суммы, один для счётчика байтов, а также получим адрес памяти с сохраненными данными скратчпада
1 2 3 4 |
*/ move r2, 0x00 //init CRC Shift register to zero / char crc = 0x00; move r0, 0 //Bytes counter / for(int i=0 move r1, scratchpad_memory //Where Bytes are stored |
Организуем метку, чтобы двигаться по байтам скратчпада, сохраним регистр R0 в стеке, заберём из скратчпада значение очередного байта и продвинемся по скратчпаду дальше
1 2 3 4 5 |
move r1, scratchpad_memory //Where Bytes are stored checkCRC8_bytes_loop: push r0 //Save counter and reuse R0 for other things ld r0, r1, 0 add r1, r1, 1 // Go to next Byte / data++ |
Далее нам нужна будет процедура для операции исключающего ИЛИ, так как в ассемблере нашем нет такой инструкции. Добавим для этого макрос
1 2 3 4 5 6 7 8 9 10 |
#include "soc/sens_reg.h" .macro xor rs rx ry or \rs, \rx, \ry push \rs and \rs, \rx, \ry move \ry, \rs pop \rx sub \rs, \rx, \ry .endm |
Вернёмся в нашу процедуру checkCRC8_bytes, сохраним теперь в стек значение регистра R1 и далее по алгоритму
1 2 3 4 5 6 7 8 |
add r1, r1, 1 // Go to next Byte / data++ push r1 // Save romid reference and reuse R1 for XOR //extract = *data; //for (char tempI = 8; tempI; tempI--) //{ // sum = (crc ^ extract) & 0x01; xor r1, r2, r0 // XOR Macro needs three registers in order to work move r2, r1 // Move result back to R2, R2 always store CRC shift register |
Ниже добавим ещё одну процедуру, которая будет заниматься одним байтом и в которой мы пока что организуем цикл следования по битам байта
1 2 3 4 5 6 |
checkCRC8_oneByte: stage_rst one_byte_loop: jumps one_byte_loop, 8, LT ret |
В секции bss объявим мароподстановку для полинома
1 2 |
.bss .set polynomial_reflected, 0b10001100 |
Также объявим ещё две переменные
1 2 3 4 5 6 7 8 9 10 |
dataR0: .long 0 .global crc8_check crc8_check: .long 0 .global crc8_value crc8_value: .long 0 |
Вернёмся в процедуру checkCRC8_oneByte и пройдёмся по нашему исследуемому байту согласно алгоритма
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
one_byte_loop: move r1, polynomial_reflected and r0, r2, 0x01 // Loads fall-off bit before rsh. rsh r2, r2, 1 //Shifting right by 1 bit, this fall-off bit was saved. //for (char tempI = 8; tempI; tempI--) //{ // sum = (crc ^ extract) & 0x01; // crc >>= 1; // if (sum) // crc ^= 0x8C; jumpr bit0_no_XOR, 1, lt bit1_do_XOR: xor r0, r2, r1 move r2, r0 bit0_no_XOR: stage_inc 1 |
Теперь перейдём в процедуру checkCRC8_bytes и вызовем нашу процедуру просчета байта
1 2 3 4 |
move r2, r1 // Move result back to R2, R2 always store CRC shift register psr jump checkCRC8_oneByte |
Вернём значения регистров из стека, проинкрементируем счётчик и перейдём на метку в случае, если не достигли последнего байта
1 2 3 4 5 6 |
jump checkCRC8_oneByte pop r1 pop r0 add r0, r0, 1 //i++) jumpr checkCRC8_bytes_loop, 8, lt //i<length; |
В процедуре get_temp вызовем нашу процедуру
1 2 3 4 5 6 7 |
jumps get_temp_9bytes, 9, LT psr jump rst_pulse psr jump checkCRC8_bytes |
Запишем в переменную значение рассчитанной контрольной суммы
1 2 3 4 |
jump checkCRC8_bytes move r1, crc8_check st r2, r1, 0 |
Затем из последнего байта принятого скратчпада заберём и запишем в переменную принятую контрольную сумму
1 2 3 4 5 6 7 |
st r2, r1, 0 move r1, scratchpad_memory add r1, r1, 8 ld r1, r1, 0 move r2, crc8_value st r1, r2, 0 |
В функции app_main файла main.c удалим данную строку с кодом
printf("Data R0: 0x%04x\n", (unsigned int) ulp_dataR0 & UINT16_MAX);
Вместо этого отобразим обе контрольные суммы
1 2 3 |
printf("ULP wakeup\n"); printf("CRC8 Checked result: 0x%02x\n", (unsigned int) ulp_crc8_check & UINT16_MAX); printf("CRC8 Scratchpad value: 0x%02x\n", (unsigned int) ulp_crc8_value & UINT16_MAX); |
Мы видим, что сборка у нас теперь происходит с ошибкой
Это означает нехватку памяти. Сейчас мы это подправим и выделим побольше. Откроем файл sdkconfig.defaults.esp32 и удвоим здесь выделение памяти
CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=2048
Соответственно, файл sdkconfig необходимо будет удалить, иначе ничего это не применится.
Пересоберём проект, обязательно перед этим сделав Clean. Теперь всё нормально.
Прошьём контроллер и посмотрим результат в терминале
Рассчитанная сумма сходится с принятой.
Отлично, теперь давайте немного разобьём на модули файл ulp_assembly_source_file.S, а то очень уж много там всего нагромождено.
Создадим файл macro.S в каталоге ulp и перенесём туда все макросы из файла ulp_assembly_source_file.S (именно перенесём — с удалением из файла-источника)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
.macro xor rs rx ry or \rs, \rx, \ry push \rs and \rs, \rx, \ry move \ry, \rs pop \rx sub \rs, \rx, \ry .endm .macro push rx st \rx,r3,0 sub r3,r3,1 .endm .macro pop rx add r3,r3,1 ld \rx,r3,0 .endm .macro psr sr=r1 pos=. .set _next2,(\pos+16) move \sr,_next2 push \sr .endm .macro ret sr=r1 pop \sr jump \sr .endm .macro GPIO_IN psr jump set_in_one_wire_pin .endm .macro GPIO_OUT psr jump set_out_one_wire_pin .endm .macro GPIO_HIGH psr jump set_one_wire_pin .endm .macro GPIO_LOW psr jump clear_one_wire_pin .endm |
В файле ulp_assembly_source_file.S подключим наш новый файл
1 2 3 |
#include "soc/sens_reg.h" #include "macro.S" |
Создадим также файл crc8.S также в каталоге ulp. В данном файле будет всё то, что связано с контрольной суммой. Подключим там для начала файл с макросами и создадим две секции
1 2 3 4 5 |
#include "macro.S" .bss .text |
В секцию bss перенесём макроподстановку и две переменные из файла ulp_assembly_source_file.S
1 2 3 4 5 6 7 8 9 10 |
.bss .set polynomial_reflected, 0b10001100 .global crc8_check crc8_check: .long 0 .global crc8_value crc8_value: .long 0 |
А в секцию text перенесём из файла ulp_assembly_source_file.S две процедуры, добавив к их заголовкам метки global
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
.text .global checkCRC8_bytes checkCRC8_bytes: /* char CRC8(const char *data,int length) { char crc = 0x00; char extract; char sum; for(int i=0;i<length;i++) { extract = *data; for (char tempI = 8; tempI; tempI--) { sum = (crc ^ extract) & 0x01; crc >>= 1; if (sum) crc ^= 0x8C; extract >>= 1; } data++; } return crc; } */ move r2, 0x00 //init CRC Shift register to zero / char crc = 0x00; move r0, 0 //Bytes counter / for(int i=0 move r1, scratchpad_memory //Where Bytes are stored checkCRC8_bytes_loop: push r0 //Save counter and reuse R0 for other things ld r0, r1, 0 add r1, r1, 1 // Go to next Byte / data++ push r1 // Save romid reference and reuse R1 for XOR //extract = *data; //for (char tempI = 8; tempI; tempI--) //{ // sum = (crc ^ extract) & 0x01; xor r1, r2, r0 // XOR Macro needs three registers in order to work move r2, r1 // Move result back to R2, R2 always store CRC shift register psr jump checkCRC8_oneByte pop r1 pop r0 add r0, r0, 1 //i++) jumpr checkCRC8_bytes_loop, 8, lt //i<length; ret .global checkCRC8_oneByte checkCRC8_oneByte: stage_rst one_byte_loop: move r1, polynomial_reflected and r0, r2, 0x01 // Loads fall-off bit before rsh. rsh r2, r2, 1 //Shifting right by 1 bit, this fall-off bit was saved. //for (char tempI = 8; tempI; tempI--) //{ // sum = (crc ^ extract) & 0x01; // crc >>= 1; // if (sum) // crc ^= 0x8C; jumpr bit0_no_XOR, 1, lt bit1_do_XOR: xor r0, r2, r1 move r2, r0 bit0_no_XOR: stage_inc 1 jumps one_byte_loop, 8, LT ret |
Проект наш, конечно же, не соберётся.
Нужно подключить новый файл в файле CMakeLists.txt, находящийся в каталоге main
set(ulp_s_sources "ulp/ulp_assembly_source_file.S" "ulp/wake_up.S" "ulp/crc8.S")
Теперь можно будет пересобрать наш проект, предварительно его очистив, и у нас по-прежнему будет всё работать, но код будет уже немного компактнее.
Итак, на данном уроке мы научились подсчитывать 8-битную контрольную сумму при помощи ассемблерного кода, что в дальнейшем нам позволит проверять правильность принятых данных. Также мы немного поупражнялись лишний раз с ассемблером.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Датчик температуры DS18B20 в экране с проводом можно приобрести здесь DS18B20
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий