Перейти к содержанию
    

Stolbov

Участник
  • Постов

    41
  • Зарегистрирован

  • Посещение

Репутация

0 Обычный

Информация о Stolbov

  • Звание
    Участник
    Участник
  • День рождения 28.06.1992

Контакты

  • Сайт
    Array
  • ICQ
    Array

Информация

  • Город
    Array

Посетители профиля

1 058 просмотров профиля
  1. Благодарю всех за ответы! Я понял, что слишком рано взялся за семафоры вообще, так как о данной ОСРВ имею лишь поверхностное представление. Начал читать материалы Курница и скачал с оф.сайта мануал по FreeRTOS в pdf, т.к. пока не пойму функционал ОС, я просто толком ничего не смогу сделать. Спасибо всем, кто потратил на мой вопрос немного времени.
  2. Это да, я сейчас всё на макетной плате собираю. Конденсаторы параллельно своим кнопкам не ставил. Спасибо! Т.е. и от прерываний тоже можно избавиться?
  3. Всем добрый день! На работе поручили заняться разработкой нового прибора на микроконтроллере STM32 (исходя из технической задачи мой выбор пал на STM32F407VE) с обязательным условием - использование ОСРВ FreeRTOS. С микроконтроллерами знаком, разрабатывал на их основе пару приборов, но с данной ОСР не работал никогда, и поэтому хочу попросить здесь совета. Работаю в STM32CubeIDE. Одна из возможностей моего девайся - это ручное управление коллекторным двигателем. В проекте я настроил две ножки на внешние прерывания GPIO_EXTI_3 и GPIO_EXTI_4. Включена подтяжка по питанию и срабатывание прерывания по любому изменению фронта сигнала (прибор должен реагировать на нажатие/отпускание кнопок. Пока одна из кнопок нажата - крутим вверх или вниз, а если отпустили, то остановка). Схемотехнически нажатие кнопки коммутирует вход МК с землёй. Программно я себе это представлял так: Срабатывание прерывания на одном из входов -> В перывании определяем, какой именно фронт пришёл -> Если фронт низкий, то крутим двигатель || Если фронт высокий, то останавливаемся. Вставлять в прерывание функцию вращения двигателем я, разумеется не стал. Вместо этого я решил в зависимости от статуса кнопки пересылать в функцию управления двигателем семафор. Условно говоря, код выглядит так: void EXTI3_IRQHandler(void) //Кнопка вращения двигателя вверх { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); if(HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_3) == GPIO_PIN_SET) //Если на входе высокий уровень, то пока ничего не делаем { asm("nop"); } else //Если уровень изменился, то { xSemaphoreGiveFromISR(sem_curr_upHandle,NULL); //Даём семафор из прерывания } } // void EXTI4_IRQHandler(void) //Кнопка вращения двигателя вниз { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); if(HAL_GPIO_ReadPin(GPIOD,GPIO_PIN_4) == GPIO_PIN_SET) //Если высокий уровень, то ничего не делаем { asm("nop"); } else //В противном случае выдаём семафор { xSemaphoreGiveFromISR(sem_curr_downHandle,NULL); } } Задача, в которой я собираюсь вращать двигателем, выглядит так: void poll_button(void const * argument) { for(;;) { if(xSemaphoreTake(sem_curr_upHandle,portMAX_DELAY) == pdPASS) //Если семафор на вращение вверх принят функцией и равен одному (время ожидания семафора максимальное) { HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_SET); } else if(xSemaphoreTake(sem_curr_downHandle,portMAX_DELAY) == pdPASS) //Если семафор на вращение вниз принят функцией и равен одному (время ожидания семафора максимальное) { HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET); } } } В качестве отлаживающего механизма использую светодиоды (их и пытаюсь зажечь на отладочной плате в зависимости от полученного семафора), но после прошивки платы ничего не происходит. Я понимаю, что код ещё не дописан, так как после отжатия кнопки мне надо вернуть семафор обратно (правильно ли - "вернуть"?), но по идее данная конструкция уже должна его "выдать", а задача - "принять", и в соответствии с этим что-то сделать. Поэтому мои вопросы заключаются в следующем: 1. Правильна ли моя идея с использованием семафоров в принципе для данной ситуации? В любом случае, эту ОСРВ мне надо изучать по работе; 2. После отдачи семафора по нажатию кнопки - как его вернуть обратно после её отжатия? 3. Задача poll_button не реагирует на полученный семафор. Если в одном из условий изменить "== pdPass" на "!= pdPass", то светодиод будет гореть, но если так сделать в двух условиях, то всё равно будет гореть только один светодиод, будто бы задача по какой-то причине зависает. Что это может быть? Заранее всех благодарю за ответы.
  4. Доброе утро! К сожалению, да. Подключил к ЛАТРу нагрузку, включил его, и гудение остаётся. С увеличением Uвых на ЛАТРе оно усиливается. а когда когда Uвых = 0, то оно не прекращается, но становится более тихим. При отключении конденсаторов ЛАТР не издаёт ни звука.
  5. Всем добрый день! Имеется ЛАТР, который выдаёт напряжение от 0 до 250V и ток до 20A. У него имеется система компенсации реактивной мощности, которая состоит из конденсаторов и твердотельных реле (они подключны параллельно нагрузке). Схему прилагаю, но схему управления реле я не рисовал, на них просто через переключатель подаётся +12В. Перед началом работы ЛАТРа, пользователь сначала подключает конденсаторы, а потом включает сам ЛАТР. Но есть нюанс: во время работы автотрансформатор начинает жутко гудеть, но если выключить схему компенсации, всё приходит в норму, и от него не слышно ни единого звука. Стоит подключить конденсаторы - и снова начинается шум. Хотелось бы попросить помощи у знающих людей: 1) Почему автотрансформатор так реагирует на подключённую схему. 2) Можно ли как-нибудь избавиться от этого не очень приятного эффекта? Всем спасибо.
  6. Перед тем, как закрыть тему, я хотел бы подвести некоторые итоги. Нет, свою задачу я так и не решил. Пару раз я переписывал свой код, и в прошлую пятницу добился его работоспособности (AVR'ка опрашивала вольтметр раз в пару-секунд и стабильно получала ответ). Не было ни каких сомнительных задержек, и прочих "костылей". В начале этой недели всё внезапно прекратило работать. Ничего не менялось. Просто включил свою отладочную плату во вторник утром, а в мониторе, который я подключил параллельно вольтметру, либо ничего нет, либо передаётся максимум один байт сообщения. Почему это происходит, и как мне найти решение - я не знаю. На одном из форумов прочёл, что при построении таких систем микроконтроллер следует тактировать от внешнего кварца, но в своём случае я не заметил никаких улучшений. Попробую использовать другие микросхемы-аналоги MAX485 (SN75176AP, к примеру). Может быть поможет. В любом случае, всем спасибо за участие. Как только я доберусь до правильного решения, я обязательно его здесь опубликую.
  7. Вы оказались правы, это не может не работать. Я всего лишь увеличил паузу после DDRE |= (1<<PORTE5); в main() до двух 2000ms, и всё заработало. От ужасного и чудовищного switch'а избавился. Было написано, и в том месте на этом было заострено внимание. Почему я напортачил на ровном месте - ума не приложу. Сейчас разбираюсь, почему не происходит отсылка данных из условия if(get_data() == 1) (я имею ввиду приведённый Вами кусочек кода).
  8. Буду работать дальше. Потому что в когда я передавал данные в цикле из массива, на выходе я получал один-единственный байт данных.
  9. Думаю, стоит рассказать, что у меня получилось. 1) Вольтметр теперь подключен не только к микроконтроллеру через MAX485, но и к компьютеру через переходник USB -> RS485. Таким образом я отслеживаю, что вольтметр получил, и что отдал. 2) Изменил функцию отправки сообщения на вольтметр, хотя в целом принцип там остался тот же, плюс исправил некоторые недочёты, на которые мне указали ранее. 3) Нашёл интересную статью по реализации кольцевого буфера на AVR. Воспользовавшись наработками пользователя, я получил следующий код: #define F_CPU 8000000 //Состояния для автомата функции call_voltage(). #define SEND_ADDR_DEV 1 #define SEND_CODE_FNC 2 #define SEND_ADDR_HGT 3 #define SEND_ADDR_LOW 4 #define SEND_NUMB_HGT 5 #define SEND_NUMB_LOW 6 #define SEND_CRC_HIGH 7 #define SEND_CRC_LOW 8 #define EEPROM_WRITE 9 #define BUFFER_SIZE 18 //Максимальный размер кольцевого буфера (на две принятые посылки) #define BUFFER_MASK (BUFFER_SIZE - 1) //Маска обнуления //Библиотеки #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <avr/eeprom.h> unsigned char buffer_u0[BUFFER_SIZE]; //Массив, выполняющий роль кольцевого буфера unsigned char IndexStart; //Старотовый индекс unsigned char IndexEnd; //Конечный индекс void uart_0_init(); //Инициализация UART0 (MAX485) void uart_1_init(); //Инициализация UART1 для отладки void uart_0_send(uint8_t data); //Отправка байта на вольтметр void uart_1_send(uint8_t data); //Отправка байта на ПК void call_voltage(); //Отправка посылки на вольтметр unsigned char write_byte_in_ring(unsigned char byte); //Функция записи в кольцевой буфер unsigned char index_number(void); //Функция, возвращающая количество непрочитанных байт из буфера unsigned char get_data(void); //Проверка, есть данные в буфере, или нет void read_byte_from_ring(unsigned char *str, unsigned char length); //Чтение ответа из кольцевого буфера в local_buffer int global_state = 0; //Переменная для хранения состояний в call_voltage unsigned char write_byte_in_ring(unsigned char byte) { IndexEnd++; //Инкремент конечного индекса. if(IndexEnd == IndexStart) //Если конечный и стартовый индекс станут равны, то return 1; //прекращаем запись. Нам не нужно, чтобы чтобы новые данные затёрли старые. IndexEnd &= BUFFER_MASK; //Накладываем маску для обнуления, если индекс перевалил за 18. buffer_u0[IndexEnd] = byte; //Записываем байт в массив. return 0; //Возвращаем 0 в знак того, что запись прошла успешно. } unsigned char index_number(void) //Функция возвращает количество непрочитанных данных в буфере { if(IndexEnd >= IndexStart) { return (IndexEnd - IndexStart); //Возвращаем количество свободного места в кольцевом буфере } else { return ((BUFFER_SIZE - IndexStart) + IndexEnd); } } unsigned char get_data(void) { if(IndexEnd != IndexStart) //Если индексы не равны, то return 1; //Вернуть "1" (данные есть) return 0; //Иначе ничего не принято } void read_byte_from_ring(unsigned char *str, unsigned char length) //Чтение пришедших по UART данных из кольцевого буфера в local_buffer. { char i; for(i = 0; i < length; i++) { IndexStart++; IndexStart &= BUFFER_MASK; *str = buffer_u0[IndexStart]; str++; } } void uart_1_send(uint8_t data) { while(!(UCSR1A & (1 << UDRE1))); UDR1 = data; } ISR(USART0_RX_vect) { write_byte_in_ring(UDR0); //Записываем принятые данные в кольцевой буфер } void uart_0_init() { UBRR0H = 0; UBRR0L = 103; //103 - 9600 бод UCSR0B |= (1<<RXCIE0)|(1<<TXCIE0)|(1<<RXEN0)|(1<<TXEN0); //Приём и передача разрешены, прерывания тоже UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); UCSR0A |= (1<<U2X0); } void uart_1_init() { UBRR1H = 0; UBRR1L = 103; UCSR1B |= (1<<RXCIE1)|(1<<TXCIE1)|(1<<RXEN1)|(1<<TXEN1); UCSR1C |= (1<<UCSZ10)|(1<<UCSZ11); UCSR1A |= (1<<U2X1); } ISR(USART0_TX_vect) { PORTE &= ~(1<<PORTE5); //Настраиваем MAX485 на приём данных } void uart_0_send(uint8_t data) { PORTE |= (1<<PORTE5); //Настраиваем MAX485 на отправку данных UDR0 = data; while(!(UCSR0A & (1 << TXC0))); } void call_voltage() //Отправка сообщения на вольтметр { uint8_t flag = 0x01; int global_state = SEND_ADDR_DEV; while(flag != 0x00) { switch(global_state) { case SEND_ADDR_DEV: uart_0_send(0x01); global_state = SEND_CODE_FNC; break; case SEND_CODE_FNC: uart_0_send(0x04); global_state = SEND_ADDR_HGT; break; case SEND_ADDR_HGT: uart_0_send(0x00); global_state = SEND_ADDR_LOW; break; case SEND_ADDR_LOW: uart_0_send(0x06); global_state = SEND_NUMB_HGT; break; case SEND_NUMB_HGT: uart_0_send(0x00); global_state = SEND_NUMB_LOW; break; case SEND_NUMB_LOW: uart_0_send(0x02); global_state = SEND_CRC_HIGH; break; case SEND_CRC_HIGH: uart_0_send(0x91); global_state = SEND_CRC_LOW; break; case SEND_CRC_LOW: uart_0_send(0xCA); flag = 0x00; break; case EEPROM_WRITE: break; } } } int main(void) { uart_0_init(); //Инициализация UART0 uart_1_init(); //инициализация UART1 sei(); unsigned char local_buffer[9]; //Локальный буфер для хранения принятого пакета DDRE |= (1<<PORTE5); //Управляющий пин настраиваем на выход _delay_ms(1000); call_voltage(); //Отсылаем данные на вольтметр while (1) { if(get_data() == 0) //Если функция вернула 0, значит, в массиве нет новых данных { asm("nop"); //И тогда мы ничего не делаем } else if(get_data() == 1) //Если функция вернула 1, значит, какие-то данные всё же получены { read_byte_from_ring(local_buffer,index_number()); //Читаем полученные от вольтметра данные в local_buffer } else { break; } uart_1_send(local_buffer[0]); //Отсылаем данные из local_buffer на ПК } } Что получилось: 1) После старта МК через UART на ПК лезет мусор, но потом появляется ответ от вольтметра, после чего на ПК отсылается последний принятый байт от вольтметра (это уже недостаток моего кода). 2) Через переходник USB -> RS485 я вижу тот же самый ответ, который я получил от вольтметра при помощи МК. Что вызвало вопросы: 1) Я полагал, что функция read_byte_from_ring полностью заполнит local_buffer, но не уверен, что это происходит, так как на пк отсылается только первый элемент массива - local_buffer[0], и, не смотря на это, на ПК я вижу полный полученный от вольтметра пакет. Может быть, я просто ещё не до конца понимаю, как это работает. 2) Попытка изменить программу таким образом, чтобы на ПК отсылалась полученная от вольтметра посылка каждые пять секунд, успехом не увенчалась. Когда я помещаю в супер-цикл функцию call_voltage, в своём ипровизированном отладчике я вижу, что микроконтроллер отправил только самый первый байт запроса: "0x01". Больше не происходит ничего. Буду рад получать идеи и замечания. Я уверен, что цель уже совсем близко. Результаты опыта на скриншоте:
  10. Доброе утро! Не думал, что это важно, поэтому квалификатор не использовал. Простите, я немного не понял, что Вы имеете ввиду. Разве PORTE |= 1<<5 не установит сигнал высокого уровня на PORTE5? Я почему-то считал, что до момента записи первого байта в eeprom микроконтроллер успеет заполнить по прерываниям массив для входных данных, но теперь я считаю, что это ошибка. Сейчас я хочу попробовать начать проверять переменную counter в течении некоторого времени (если она станет равной 9 до истечении времени проверки, значит, посылка пришла, и её можно разбирать). Сделал так: ISR(USART0_RX_vect) { uint8_t data; if(counter > 9) counter = 0; else { data = UDR0; buffer[counter++] = data; } }
  11. Доброе утро! Возможно, это очень "профановское" отношение, но мне показалось, что до первой записи в eeprom мк успеет принять байты по прерыванию и записать их в массив. Возможно, мне просто надо было сделать проверку переменной counter. Если она равна 9 (количество байт в ожидаемой посылке), то тогда можно смело писать данные в память. Здесь мне надо было написать более подробно. Извините. Прекрасно понимаю, что телепатии не существует) Вообщем, если передачу пакета оформить вот так: //... uint8_t databuffer[8] = {0x01,0x04,0x00,0x06,0x00,0x02,0x91,0xCA}; int i = 0; while(i < 8) { uart_0_send(databuffer[i]); i++; } //... То пакет не будет отправлен, либо будет отправлен только первый байт. В чём причина этого, мне не известно. Попробую сделать так, как вы сказали. И да, конкретно в моём вольтметре нужно было настроить параметры передачи данных (количество байт данных, стоповых бит, скорость), что я и сделал. P.S. Подключил, всё получилось. Красной линией отмечена моя посылка (прочитать регистры 6 и 7), а синей - ответ от прибора. Зелёным прямоугольником отметил текущее напряжение в формате float.
  12. О, с этим проблем нету. Модель вольтметра: PD194UI-9K4T. Я подключал его через переходник к ПК, и опрашивал его при помощи его фирменной программы. Так же я его опрашивал при помощи терминала:
  13. Всем добрый вечер! Заранее хочу извиниться за длинный пост, но мне кажется, чем подробнее я всё опишу, тем будет лучше и для меня, и для тех, кто захочет мне помочь. Я делаю небольшую систему на основе микроконтроллера ATmega128, которая должна опросить электронный вольтметр по интерфейсу RS485, получить пакет, разобрать его, и на основе полученного значения выполнить некоторые действия. В качестве интерфейсной микросхемы я выбрал MAX485, но в данный момент времени я провожу свои эксперименты не на "голой" микросхеме, а на ардуиновском модуле (делаю я это по одной простой причине: на этом модуле уже есть резисторные подтяжки на линиях связи, и это всяко лучше, чем я бы ставил эти резисторы на макетную плату вокруг микросхемы). Вывод RXD0 микросхемы подключён к входу модуля R0, а вывод TXD0 - к входу DI. Входы DE и RE модуля закоротил паяльником и подключил к выводу PE5. Ниже представлен код, который я пытаюсь заставить работать: #define F_CPU 8000000 #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <avr/eeprom.h> void uart_0_init(); void uart_0_send(uint8_t data); void call_voltage(); uint8_t buffer[9]; //Буфер для хранения ответа int counter = 0; //Счётчик принятых байт void uart_0_init() //Инициализация UART { UBRR0H = 0; UBRR0L = 103; //Скорость 9600 бод, UCSR0B |= (1<<RXCIE0)|(1<<TXCIE0)|(1<<RXEN0)|(1<<TXEN0); //Приём и передача разрешены, прерывания тоже UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); UCSR0A |= (1<<U2X0); } void call_voltage() { uart_0_send(0x01); //Отправка сообщения на прибор (опрос регистра 0x0006 и 0x0007) uart_0_send(0x04); uart_0_send(0x00); uart_0_send(0x06); uart_0_send(0x00); uart_0_send(0x02); uart_0_send(0x91); uart_0_send(0xCA); eeprom_write_byte(0x00,buffer[0]); //Запись в EEPROM ответа от прибора eeprom_write_byte(0x01,buffer[1]); eeprom_write_byte(0x02,buffer[2]); eeprom_write_byte(0x03,buffer[3]); eeprom_write_byte(0x04,buffer[4]); eeprom_write_byte(0x05,buffer[5]); eeprom_write_byte(0x06,buffer[6]); eeprom_write_byte(0x07,buffer[7]); eeprom_write_byte(0x08,buffer[8]); } ISR(USART0_RX_vect) //Прерывание по приёму байт { uint8_t data = UDR0; buffer[counter++] = data; //Запись принятого байта в массив } ISR(USART0_TX_vect) //Прерывание по отправке байта из UART { PORTE &= ~(1<<PORTE5); //Установить на PE5 низкий уровень (разрешение приёма) } void uart_0_send(uint8_t data) //Функция отправки байта { PORTE &= ~(0<<PORTE5); //Установка на E5 выскокого уровня (разрешение отправки) UDR0 = data; while(!(UCSR0A & (1 << TXC0)) ); } int main(void) { uart_0_init(); //Инициализация UART sei(); //Разрешение глобальных прерываний DDRE &=~ (0<<PORTE5); //Настраиваем PE5 на выход (управление приёмом и передачей MAX485) _delay_ms(1000); call_voltage(); //Отправка сообщения на прибор while (1) { } } Как на мой взгляд всё это должно работать: на UART отправляется байт данных, перед этим на управляющий вывод подаётся высокий уровень. Когда байт покинул буфер, срабатывает прерывание по отправке, и на управляющий вывод подаётся низкий уровень, т.е. "0". Так происходит до конца отправки всего сообщения. Далее прибор должен прислать ответ, прерывание по приёму байт должно занести принятые данные в массив, после чего они все запишутся в EEPROM. Прибор принимает мой пакет, и даже отсылает ответ, это точно (во время процедуры обмена данными на его лицевой панели начинает мигать соответствующий значок), но только в том случае, если в функции call_voltage() отсутствуют функции записи данных в eeprom. Стоит их добавить, и посылка не отправляется (индикация на приборе не горит). Сразу хочу сообщить о вещах, которые мне показались очень странными: 1) Посылку отправляю именно таким образом, потому что если использовать что-то вроде этого: while(i < 8) { uart_0_send(databuffer[i]); i++; } то посылка попросту не отправится, либо отправится только её первый байт. 2) Если после инструкции DDRE &=~ (0<<PORTE5); не поставить задержку на 1000 ms, или поставить на меньшее время, посылка не отправится. 3) Отправка посылки не будет работать, если функцию call_voltage() поместить в цикл while(1) с задержкой после каждого вызова (я это проверил при помощи переходника USB -> RS485). Разумеется, этот код ужасен, но моя главная задача - получить от вольтметра хоть какую-нибудь посылку. Буду благодарен за любые советы и указания на ошибки.
  14. MegaVolt, как работает делитель на двух резисторах - мне известно. О роли операционного усилителя в этой схеме я тоже узнал - он сравнивает потенциал на выходе схемы с потенциалом в средней точке делителя и поддерживает напряжение на своём выходе таким, чтобы разность сравниваемых потенциалов была равна нулю. Вопрос ведь в том, какое напряжение мне надо подавать на вход ОУ. Ведь я могу выбрать два одинаковых резистора по одному ому, и на выходе у меня будет 2,5 вольта, а можно взять один резистор на 30 Ом, а другой - на пять, и результат следовательно тоже будет другим. Поэтому и спросил про "подобрать". Всем спасибо за участие . Буду читать книги по применению ОУ, и штудировать гугол.
×
×
  • Создать...