Jump to content

    

Stolbov

Участник
  • Content Count

    36
  • Joined

  • Last visited

Community Reputation

0 Обычный

About Stolbov

  • Rank
    Участник
  • Birthday 06/28/1992

Контакты

  • Сайт
    http://
  • ICQ
    0

Информация

  • Город
    Санкт-Петербург

Recent Profile Visitors

445 profile views
  1. Перед тем, как закрыть тему, я хотел бы подвести некоторые итоги. Нет, свою задачу я так и не решил. Пару раз я переписывал свой код, и в прошлую пятницу добился его работоспособности (AVR'ка опрашивала вольтметр раз в пару-секунд и стабильно получала ответ). Не было ни каких сомнительных задержек, и прочих "костылей". В начале этой недели всё внезапно прекратило работать. Ничего не менялось. Просто включил свою отладочную плату во вторник утром, а в мониторе, который я подключил параллельно вольтметру, либо ничего нет, либо передаётся максимум один байт сообщения. Почему это происходит, и как мне найти решение - я не знаю. На одном из форумов прочёл, что при построении таких систем микроконтроллер следует тактировать от внешнего кварца, но в своём случае я не заметил никаких улучшений. Попробую использовать другие микросхемы-аналоги MAX485 (SN75176AP, к примеру). Может быть поможет. В любом случае, всем спасибо за участие. Как только я доберусь до правильного решения, я обязательно его здесь опубликую.
  2. Вы оказались правы, это не может не работать. Я всего лишь увеличил паузу после DDRE |= (1<<PORTE5); в main() до двух 2000ms, и всё заработало. От ужасного и чудовищного switch'а избавился. Было написано, и в том месте на этом было заострено внимание. Почему я напортачил на ровном месте - ума не приложу. Сейчас разбираюсь, почему не происходит отсылка данных из условия if(get_data() == 1) (я имею ввиду приведённый Вами кусочек кода).
  3. Буду работать дальше. Потому что в когда я передавал данные в цикле из массива, на выходе я получал один-единственный байт данных.
  4. Думаю, стоит рассказать, что у меня получилось. 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". Больше не происходит ничего. Буду рад получать идеи и замечания. Я уверен, что цель уже совсем близко. Результаты опыта на скриншоте:
  5. mcheb, опечатался. Спасибо.
  6. Доброе утро! Не думал, что это важно, поэтому квалификатор не использовал. Простите, я немного не понял, что Вы имеете ввиду. Разве 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; } }
  7. Доброе утро! Возможно, это очень "профановское" отношение, но мне показалось, что до первой записи в 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.
  8. О, с этим проблем нету. Модель вольтметра: PD194UI-9K4T. Я подключал его через переходник к ПК, и опрашивал его при помощи его фирменной программы. Так же я его опрашивал при помощи терминала:
  9. Всем добрый вечер! Заранее хочу извиниться за длинный пост, но мне кажется, чем подробнее я всё опишу, тем будет лучше и для меня, и для тех, кто захочет мне помочь. Я делаю небольшую систему на основе микроконтроллера 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). Разумеется, этот код ужасен, но моя главная задача - получить от вольтметра хоть какую-нибудь посылку. Буду благодарен за любые советы и указания на ошибки.
  10. MegaVolt, как работает делитель на двух резисторах - мне известно. О роли операционного усилителя в этой схеме я тоже узнал - он сравнивает потенциал на выходе схемы с потенциалом в средней точке делителя и поддерживает напряжение на своём выходе таким, чтобы разность сравниваемых потенциалов была равна нулю. Вопрос ведь в том, какое напряжение мне надо подавать на вход ОУ. Ведь я могу выбрать два одинаковых резистора по одному ому, и на выходе у меня будет 2,5 вольта, а можно взять один резистор на 30 Ом, а другой - на пять, и результат следовательно тоже будет другим. Поэтому и спросил про "подобрать". Всем спасибо за участие . Буду читать книги по применению ОУ, и штудировать гугол.
  11. Может и классика, но до вчерашнего дня я с подобными задачами не сталкивался. Вот и приходится заниматься этим "с нуля".
  12. Plain, не могли бы Вы подсказать мне, пожалуйста, какие сопротивления резисторов и какую ёмкость конденсатора мне следует подобрать для данной схемы (U опорное - 5 вольт)? Операционный усилитель я присмотрел себе вот такой: одноканальный MCP6001.
  13. Нет, не курсовой. Во время учёбы в универе грезил о том, чтобы работать по специальности, и в конечном итоге устроился. Вот, учусь работать, набираюсь опыта. За подробное разъяснение спасибо. Думаю, сначала заставлю работать свою схему, отлажу расчёты, а потом уже буду искать другое АЦП.
  14. Не могли бы Вы рассказать по подробней, пожалуйста, как мне сделать виртуальную землю? Если я верно понял, то есть делитель напряжения из двух резисторов, с которого снимается опорное напряжение для питания АЦП, и есть операционный усилитель, на который подаётся напряжение с нагрузочного резистора, и с которого это напряжение идёт дальше на АЦП. И между ними есть точка, к которой следует подключить нагрузочный резистор. Я попробовал нарисовать схему, но я почти уверен, что она неправильная.
  15. Дали задачу с использованием именно этого элемента (второй проект на новой работе).