Reystlin 0 7 августа, 2022 Опубликовано 7 августа, 2022 (изменено) · Жалоба Видимо я что-то не так с ДМА делаю, в rx_buff заполняется первый ответный пакет и после этого данные в нём не меняются, хотя ответы разные на шине присутствуют нужно ли перед повторным включением DMA на прием сбрасывать какой-нибудь счетчик? Изменено 7 августа, 2022 пользователем Reystlin Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 1 7 августа, 2022 Опубликовано 7 августа, 2022 (изменено) · Жалоба Зачем такая дикая свистопляска с таймерами ради модбаса, когда есть регистр RTO? Получили прерывание по таймауту - выставили флаг - поменяли приемный буфер, а в суперлупе, как увидели флаг, обработали принятые данные!.. В случае с DMA нужно лишь, чтобы буферы были достаточно большими. В обработчике прерывания по таймауту отключаем DMA, меняем буферы, ставим флаг - вуаля! Изменено 7 августа, 2022 пользователем Eddy_Em Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 7 августа, 2022 Опубликовано 7 августа, 2022 · Жалоба Пофиксил. Не хватало сброса флага окончания передачи из уарта в память на приеме void USART1_IRQHandler(void) { const uint32_t sr = USART1->SR, cr = USART1->CR1; if((sr & USART_SR_TC) && (cr & USART_CR1_TCIE)) { // Окончили отправку USART1->SR = ~(USART_SR_TC); DMA2_Stream5->NDTR = 255; DMA2->HIFCR = DMA_HIFCR_CTCIF5; DMA2_Stream5_En(); GPIOA->BSRR = GPIO_BSRR_BR11; // Прием USART_status = USART_READ; // Настройка таймера на Timeout TIM7->CR1 = 0; TIM7->CNT = 0; TIM7->ARR = 100000-1; TIM7_En(); } else if((sr & USART_SR_IDLE) && (cr & USART_CR1_IDLEIE)) { // Окончили прием ответа (void)USART1->DR; USART1->SR = ~(USART_SR_IDLE); DMA2_Stream5->CR = 0; GPIOA->BSRR = GPIO_BSRR_BS11; // Прием окончен USART_status = USART_WAIT; TIM7->CR1 = 0; TIM7->CNT = 0; TIM7->ARR = 500-1; // Задержка отправки второго пакета после приема TIM7_En(); // Включаем счетчик для задержки отправки второго пакета recive_fn(rx_buf); } } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 7 августа, 2022 Опубликовано 7 августа, 2022 · Жалоба К чему эти игры с TIM7? На практике толку от них никакого не будет кроме напрасной траты таймера. Я не заметил паузу после перевода трансмиттера на передачу перед началом отправки. Без неё ждут ошибки передачи, особенно на высоких скоростях. P. S. Посмотри на стиль своей программы и подумай, как у тебя будет выглядеть программа, если вдруг тебе понадобится сделать, например, 4 мастера и 2 слэйва? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба Задачи multimaster не стоит а с несколькими slave не вижу проблемы. использую дальше вот так enum ModBUS_packet_status{ IDLE, SEND, SEND_ERROR, WAIT_ANSWER, ANSWER_OK, ANSWER_ERROR, DONE }; enum ModBUS_Error{ NONE, TIMEOUT, SLAVE_ID_ERROR, CMD_ERROR, FUNCTION_CODE_ERROR, ACCESS_ADDRESS_ERROR, DATA_ERROR, CRC_ERROR }; enum ModBUS_cmd{ //Read_Coil_Status = 0x01, //Read_Input_Status = 0x02, Read_Holding_Registers = 0x03, //Read_Input_Registers = 0x04, //Force_Single_Coil = 0x05, Preset_Single_Register = 0x06, //Force_Multiple_Coils = 0x0F, //Preset_Multiple_Registers = 0x10 }; #define MODBUS_QUEUE_SIZE 2 volatile struct _ModBUS_packet_queue { uint8_t SlaveID; enum ModBUS_cmd cmd; uint16_t address; uint16_t request_data; uint32_t answer_data; enum ModBUS_packet_status status; enum ModBUS_Error error; }ModBUS_packet_queue[MODBUS_QUEUE_SIZE]= {{1, 0x03, 0x602C, 0x0002, 0, 0, 0}, {2, 0x03, 0x602C, 0x0002, 0, 0, 0}};/* {2, 0x03, 0x0B1C, 0x0002, 0, 0, 0}, {2, 0x03, 0x0B1D, 0x0002, 0, 0, 0} };*/ volatile uint8_t read_queue_USART_pointer; uint8_t Check_Error(uint8_t *Response) { if(Response[1]&0x80) return Response[2]; return 0; // Error code // 0x01 - Function code error // 0x02 - Access address error // 0x03 - Data error, such as write data exceeding the limit // 0x08 - CRC check error } void Recive_ModBUS(uint8_t *Response) { if(USART_Get_Error()== USART_NO_ERROR) { if(Response[0] != ModBUS_packet_queue[read_queue_USART_pointer].SlaveID) { ModBUS_packet_queue[read_queue_USART_pointer].answer_data = 0; ModBUS_packet_queue[read_queue_USART_pointer].error = SLAVE_ID_ERROR; } else if(Check_Error(Response)) { ModBUS_packet_queue[read_queue_USART_pointer].answer_data = 0; if(Check_Error(Response) & 0x01) ModBUS_packet_queue[read_queue_USART_pointer].error = FUNCTION_CODE_ERROR; else if(Check_Error(Response) & 0x02) ModBUS_packet_queue[read_queue_USART_pointer].error = ACCESS_ADDRESS_ERROR; else if(Check_Error(Response) & 0x03) ModBUS_packet_queue[read_queue_USART_pointer].error = DATA_ERROR; else if(Check_Error(Response) & 0x04) ModBUS_packet_queue[read_queue_USART_pointer].error = CRC_ERROR; } else if(Response[1] != Read_Holding_Registers) { ModBUS_packet_queue[read_queue_USART_pointer].answer_data = 0; ModBUS_packet_queue[read_queue_USART_pointer].error = CMD_ERROR; } else { ModBUS_packet_queue[read_queue_USART_pointer].answer_data = (Response[4]<<24) | (Response[5]<<16) | (Response[6]<<8) | Response[7]; } if(ModBUS_packet_queue[read_queue_USART_pointer].error) ModBUS_packet_queue[read_queue_USART_pointer].status = ANSWER_ERROR; else ModBUS_packet_queue[read_queue_USART_pointer].status = DONE; } else { ModBUS_packet_queue[read_queue_USART_pointer].status = SEND_ERROR; ModBUS_packet_queue[read_queue_USART_pointer].error = TIMEOUT; } if(++read_queue_USART_pointer == MODBUS_QUEUE_SIZE) read_queue_USART_pointer = 0; } void Send_ModBUS() { if(ModBUS_packet_queue[read_queue_USART_pointer].status != SEND) { char msg[8]; uint16_t reg = ModBUS_packet_queue[read_queue_USART_pointer].address; uint16_t data = ModBUS_packet_queue[read_queue_USART_pointer].request_data; msg[0] = ModBUS_packet_queue[read_queue_USART_pointer].SlaveID; msg[1] = ModBUS_packet_queue[read_queue_USART_pointer].cmd; msg[2] = (uint8_t)(reg>>8); msg[3] = (uint8_t)(reg&0xFF); msg[4] = (uint8_t)(data>>8); msg[5] = (uint8_t)(data&0xFF); uint16_t crc = ModBUS_CRC16((uint8_t*)msg,6); msg[6]=(uint8_t)(crc&0xFF); msg[7]=(uint8_t)(crc>>8)&0xFF; if(USART1_put(8, msg, Recive_ModBUS) == USART_SEND) ModBUS_packet_queue[read_queue_USART_pointer].status = SEND; } } void ModBUS_Exec() { Send_ModBUS(); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба 1 hour ago, Reystlin said: Задачи multimaster не стоит а с несколькими slave не вижу проблемы. Причём тут мультимастер? На четырёх портах сидят четыре мастера. Ещё на дух портах сидят два слэйва. Итого задействовано 6 штук UART. Таймеров-то хватит? :) Я в ПЛК ещё сделал статистику запросов. На практике полезно при поиске несправностей в сети. Для примера фрагмент карты памяти простого ПЛК с двумя UART, каждый может быть сконфигурирован как мастер, или как слэйв. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба у меня задача попроще. есть 10 серводвигателей и нужно постоянно циклически их положение получать. Без таймера нормально не работает - нужна задержка между отправкой текущего пакета и следующего. так-же таймаут нужно как-то определять Про паузу после перевода трансмиттера в режим передачи и отправку пакета добавлю. спасибо Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба 2 часа назад, Reystlin сказал: у меня задача попроще. есть 10 серводвигателей и нужно постоянно циклически их положение получать. Без таймера нормально не работает - нужна задержка между отправкой текущего пакета и следующего. Это всё элементарно делается на одном таймере. 2 часа назад, Reystlin сказал: Про паузу после перевода трансмиттера в режим передачи и отправку пакета добавлю. спасибо Я про неё ещё несколько десятков постов назад писал. Странно что не заметили..... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 1 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба Неужели у уартов STM32 нет флага, который выдерживал бы заданную паузу после передачи последнего символа из посылки DMA? Ну, даже если так, можно кривой модбас хоть на шести уартах одновременно реализовать при помощи всего одного систика, просто счетчики отдельные завести на каждый уарт. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 8 августа, 2022 Опубликовано 8 августа, 2022 · Жалоба 39 minutes ago, jcxz said: Это всё элементарно делается на одном таймере. Так у меня и так на одном таймере, на TIM7 оба события.... в зависимости от состояния перестраиваю время срабатывания прерывания. 40 minutes ago, jcxz said: Я про неё ещё несколько десятков постов назад писал. Странно что не заметили..... Видимо проглядел... спасибо Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 9 августа, 2022 Опубликовано 9 августа, 2022 (изменено) · Жалоба 9 hours ago, Reystlin said: Без таймера нормально не работает - нужна задержка между отправкой текущего пакета и следующего. Если МК мастер, то тебе достаточно перед отправкой сделать паузу >1.75мс (заметь, "больше", а не строго "равно" ), что при работе под FreeRTOS не вызывает проблем. Пишешь код под ОС РВ, а софтовые таймеры ОС упорно игнорируешь, хотя тут они очень удобны. Изменено 9 августа, 2022 пользователем tonyk_av Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 1 9 августа, 2022 Опубликовано 9 августа, 2022 · Жалоба Лучше кривой модбас реализовать в виде конечного автомата: ты ему в кольцевой буфер отправляешь структуры данных, которые нужно будет переслать, а он уже самостоятельно по прерываниям контролирует паузы и т.д., и т.п. В суперлупе каждый проход вызывать этот modbus_process(). Вот с приемом намного проще, двух буферов-структур хватит: заполняешь себе посредством DMA один буфер, как только получил прерывание idle, выставляешь флаг готовности буфера и начинаешь заполнять второй. А в суперлупе какой-нибудь modbus_incoming() будет обрабатывать данные из этого буфера. В общем, даже если требуется несколько усартов контролировать, все это элементарно решится одним систиком и парой указанных функций (а там уж внутри будет рассматриваться источник и т.д., и т.п.) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 9 августа, 2022 Опубликовано 9 августа, 2022 · Жалоба 12 часов назад, Reystlin сказал: Так у меня и так на одном таймере, на TIM7 оба события.... Я говорил про случай работы с несколькими каналами связи параллельно (несколькими UART). О такой работе тут выше говорили другие люди. Ваш код имеет жёсткую связь UART-таймер, а значит потребует отдельного таймера на каждый новый UART. Что излишне расточительно. PS: Также нет никакой обработки ошибок. А если например IDLE появился не из-за того, что входящий пакет закончился, а из-за обрыва на линии? Также нет никакой обработки коллизий событий от таймера и UART. Например запустился отсчёт времени приёма; RX-пакет приходит в самом конце таймаута; получаете прерывание IDLE от UART чуть раньше, а запрос на прерывание от таймера приходит когда находитесь в ISR UART. Что будет? каков приоритет у ISR UART и ISR таймера? Предположим у таймера приоритет <= приоритету UART. Тогда сразу после выхода из ISR UART получите прерывание от таймера, которое переведёт вашу переменную состояния в IDLE (хотя в это время вроде должна идти выдержка на передачу). Если приоритет ISR таймера выше чем у UART - тоже ничего хорошего - опять катавасия. Так как нету корректного взаимодействия между обработчиками событий UART и таймера. И нету чёткой отработки машины состояний. И вообще всё очень сыро и плохо продумано.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 44 9 августа, 2022 Опубликовано 9 августа, 2022 · Жалоба 1 hour ago, jcxz said: И вообще всё очень сыро и плохо продумано.... То есть не мне одному видится, что ТС не продумал как строить Модбас-мастер. Он посложнее слэйва будет. 1 hour ago, jcxz said: Также нет никакой обработки ошибок Она тут особо не нужна. В Модбас-фрейме есть длина блока данных и контрольная сумма. Их ведь всё равно проверять перед обработкой запроса. ИМХО, ТС упорно игнорирует возможности FreeRTOS, поэтому у него получается громоздкий и негибкий код в части отработки таймаутов. 3 hours ago, Eddy_Em said: модбас реализовать в виде конечного автомата Вот это хороший способ. У меня так и сделано. Работает в отдельной задаче. Всё получается очень просто: быстренько прошёлся по состояниям и через yield() вернул время ОС. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Reystlin 0 9 августа, 2022 Опубликовано 9 августа, 2022 · Жалоба 13 hours ago, tonyk_av said: Если МК мастер, то тебе достаточно перед отправкой сделать паузу >1.75мс (заметь, "больше", а не строго "равно" ), что при работе под FreeRTOS не вызывает проблем. Пишешь код под ОС РВ, а софтовые таймеры ОС упорно игнорируешь, хотя тут они очень удобны. у меня нет ОС.... не добавлять же её только из-за работы с ModBUS Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться