загнал программу в AVR studio - как то странно себя ведет немного. поначалу, когда инициализируется УАРТ - все хорошо. как только доходит до этих строчек
UCSRC=(1<<USBS)|(1<<UCSZ1)|(1<<UCSZ0);
UCSRB=(1<<RXEN)|(1<<TXEN);
EnableReceive0;
InitTimer0;
StartTimer0;
так выставляется 2 стоп бита, меняется !!битрейт!! и после этого программ идет в main, где и висит. вот что в УАРТе, когда main по кругу гоняется
поменял, глюк остался. уже с другими значениями. таймер тоже поменял, да, не заметил сразу. а нужны ли такие сложности с настройкой UART, если эта настройка выполняется только при начале цикла программы, а затем уже обновляется?. Глюк проявляется при прохождении строчки EnableReceive0;
Hold[On] писал(а):поменял, глюк остался. уже с другими значениями. таймер тоже поменял, да, не заметил сразу. а нужны ли такие сложности с настройкой UART, если эта настройка выполняется только при начале цикла программы, а затем уже обновляется?. Глюк проявляется при прохождении строчки EnableReceive0;
Сложностей с настройкой UARTA нет, либо вы его настраиваете и он работает, либо нет. В чем заключается глюк? Чего я не заметил , смотря на вашу картинку )?
после строчки EnableReceive0; меняется значение значение старшего регистра, который отвечает за скорость передачи. скорост 19200 соответсвует только младщий 0х33 (16мгц/16)/19200 = 0x33. щас пытаюсь по шагам запихивать туда через уарт нужную строку, например на чтение 0х20 0х03 0х00 0х01 0х00 0х01 0хD3 0x7B, посмотрю как себя поведет.
и еще есть пара непонятностей. как только приходит первый байт с адресом начинает выполнеться процедура:
if (!StartRec)
{ StartRec=true;
RcCount=0;
DataPause=0;
cmRcBuf0[RcCount++]=cTempUART;
StartTimer0;
}
то есть адрес у нас записывается в массив, с номером 1, т.к.RcCount++ уже примет значение 1.
а когда переполняется таймер, и начинается проверка slave-адреса, почему то проверяется нулевое значение массива:
if (cmRcBuf0[0]!=0x20) return 0x00
следовательно дальше этого места у меня ничего не идет, а возвращается в основной цикл.
и перезапуск таймер TCNT0=dTCNT0, при приеме байтов после адреса не происходит - он продолжает крутиться, с того места, с какого подошел к этому TCNT0=dTCNT0.
беру свои слова обратно - я неверно выставлял данные в UDR (выставлял прерывание и сразу же данные, которые тут же обнулялись). тестирую дальше. тяжела и неказиста..жисть студента
спустя 20 минут:
вбиваю вручную данные, которые посылает Modbus Poll
на чтение - 20 03 00 01 00 01 D3 7B
и на запись
20 06 00 01 00 00 DE BB
вроде все идет нормально. но вот настройки UART не нравится.
спустя полчаса:
и чтение и запись идет нормально, регистр меняется в зависимости от получаемых данных. щас попробую на железе. уже не знаю где может быть подвох.
спустя еще какое то время - лучи радости озарили мое лицо. как я и говорил - проблема была в настройкеUART.сделал тка:
EnableReceive0;
InitTimer0;
StartTimer0;
}
отсылает байт на контроллер нормально - горит нужный светодиод. а вот с приемом - есть проблемы. пишет ошибки типа:
Frame error
Break condidion
работаем дальше)
ну, вот и все. Протокол полностью работает, опрос порта поставил 50мс - ни одной ошибки, провод 15 метров, скорость 19200. Спасибо огромное за помощью, без вас бы не справился. Моя научная работа спасена) Осталось наваять програмку в TraceMode, графическую оболочку, и можно спокойно жить. Вот полностью рабочий исходник для ATmega16, компилятор IAR.
Проверялось с помощью программы MODBUS Poll, её настройки:
Com1
скорость 19200
паритет - none
стоп бит - 1
частота опроса-50мс
Delay between polls - не совсем понял за что отвечает, выставлено 2 мс
теперь по функции
Slave ID - 32
Функция - 03 HOLDING REGISTER
адрес - 1
"квонтити" - 1
частота опроса - ставил 10 мс, тоже работало.
А кто-нибудь делал реализацию ModBus rtu master на микроконтроллере? Мне вот нужно связать пульт управления на ATmega32 с контроллером (контроллерами) на ATmega128. Со slave особых проблем не возникло, а вот как сделать master пока не додумал.
Ну, я делал - и неоднократно.
Одно понять не могу : одна линия, на одном конце slave - нет проблем, на другом master, протокол тот же - и вдруг проблемы. Создаем сами, чтоб пото'м героически их преодолевать ?
Подчиненные контроллеры - промышленные или свои ? В любом случае надо внимательно изучить их протокол и передавать/принимать им/от них то, что требуется по протоколу.
На абстрактный вопрос - абстрактный ответ.
помогите не могу понять в чем дело. modbus poll пишет Timeout error. использовал исходник, который выложил и заверил что работает Hold[On]. modbus poll настраивал так
Com1
скорость 19200
паритет - none
стоп бит - 1
частота опроса-1000мс
Slave ID - 1
Функция - 03 HOLDING REGISTER
адрес - 1
квонтити -1
в исходнике изменил частоту кварца с 16000000 на 11059200 Гц. соответственно пересчитал таймер счетчик 0, и поменял настройку УСАРТа (UBRRL=0x35 для 11059200 Гц 19200);. код подправил для AVR studio, заменил только вектора прерывания
#define dXTAL 11059200
#include <stdio.h>
#include <ina90.h>
#include <avr/interrupt.h> // для обработки пррерывания
#include "stdbool.h"
//размер буфера принимаемых по UART данных
#define MaxLenghtRecBuf 25
//размер буфера передаваемых по UART данных
#define MaxLenghtTrBuf 25
//обработка посылки
if (cmRcBuf0[0]!=0x01) return 0x00; //адрес устройства //ответ не нужен
CRC16=GetCRC16(cmRcBuf0,NumByte-2);//подсчет CRC в принятой посылке
TempI=(int) (cmRcBuf0[NumByte-1]<<8) + cmRcBuf0[NumByte-2];
if (CRC16!=TempI) return 0x00; //контрольная сумма //ответ не нужен
cmTrBuf0[0]=0x01;//адрес устройства
//код команды
switch(cmRcBuf0[1]){
case 0x03:{//чтение регистров
TempI=(int) (cmRcBuf0[2]<<8) + cmRcBuf0[3];
if (TempI!=1){ //првоерка номера регистра, есть только 1 регистр
return ErrorMessage(0x02); //данный адрес не может быть обработан
}
TempI=(int) (cmRcBuf0[4]<<8) + cmRcBuf0[5];
if (TempI!=1){//проверка кол-ва запрашиваемых регистров, есть только 1 регистр
return ErrorMessage(0x02); //данный адрес не может быть обработан
}
cmTrBuf0[1]=0x03;//команда
cmTrBuf0[2]=0x02;//кол-во байт данных
cmTrBuf0[3]=0x00;//старший байт
TempI=PINB;
cmTrBuf0[4]=Low(TempI);//уровни порта F
TempI=GetCRC16(cmTrBuf0,5);//подсчет КС посылки
cmTrBuf0[5]=Low(TempI);
cmTrBuf0[6]=Hi(TempI);
return 7;
}
case 0x06:{//запись в единичный регистр
TempI=(int) (cmRcBuf0[2]<<8) + cmRcBuf0[3];
if (TempI!=1){ //првоерка номера регистра, есть только 1 регистр
return ErrorMessage(0x02); //данный адрес не может быть обработан
}
TempI=(int) (cmRcBuf0[4]<<8) + cmRcBuf0[5];
if (TempI>0xFF){ //проверка числа, которое надо записать в порт
return ErrorMessage(0x03); //недопустимые данные в запросе
}
PORTB=Low(TempI);
cmTrBuf0[1]=cmRcBuf0[1];//команда
cmTrBuf0[2]=cmRcBuf0[2];//адрес
cmTrBuf0[3]=cmRcBuf0[3];//
cmTrBuf0[4]=cmRcBuf0[4];//данные
cmTrBuf0[5]=cmRcBuf0[5];//
cmTrBuf0[6]=cmRcBuf0[6];//КС
cmTrBuf0[7]=cmRcBuf0[7];//
return 8;
}
default:{
return ErrorMessage(0x01); //недопустимая команда
}
}
}//end ModBus()
//запретить прерывание приема
#define DisableReceive0 ClrBit(UCSRB,RXEN); ClrBit(UCSRB,RXCIE)
//разрешить прерывание приема
#define EnableReceive0 SetBit(UCSRB,RXEN); SetBit(UCSRB,RXCIE)
//проверка на разрешение прерывания приема
#define TestReceive0 TestBit(UCSRB,RXCIE)
//разрешить прерывание по освобождению буфера передачи, начать передачу
#define GoTrans0 SetBit(UCSRB,TXEN); SetBit(UCSRB,UDRIE)
//запретить прерывание по освобождению буфера передачи, остановка передачи
#define StopTrans0 ClrBit(UCSRB,TXEN); ClrBit(UCSRB,UDRIE)
//проверка на разрешение прерывания передачи
#define TestTrans0 TestBit(UCSRB,UDRIE)
char cTempUART;
ISR(USART_RXC_vect) //адрес прерывания приема UART
{
cTempUART=UDR;
if (UCSRA&(1<<FE)) return; //FE-ошибка кадра, OVR - переполнение данных (флаги)
if (!StartRec){ //если это первый байт, то начинаем прием
StartRec=true;\
RcCount=0;
DataPause=0;
cmRcBuf0[RcCount++]=cTempUART;
StartTimer0;
}else{// end if (StartRec==0) //продолжаем прием
if (RcCount<MaxLenghtRecBuf){//если еще не конец буфера
cmRcBuf0[RcCount++]=cTempUART;
PORTA=0x01;
}else{//буфер переполнен
cmRcBuf0[MaxLenghtRecBuf-1]=cTempUART;
}
DataPause=0;
TCNT0=dTCNT0;//перезапуск таймера
}//end else if (StartRec==0)
}//end __interrupt UART0_RX_interrupt()
ISR(SIG_OVERFLOW0) //адрес прерывания таймера/счетчика 0 по переполнению
{
if (StartRec){
PORTA=0;
StartRec=false; //посылка принята
cNumRcByte0=RcCount; //кол-во принятых байт
Не зачем писать код для AVR с нуля ... есть замечательная библиотека libmodbus написанная на С ... библиотека распространяется в двух версиях GPL и проприетарная ... тут Modbus AVR есть хорошая статья на русском и код бесплатной версии с добавленной поддержкой нескольких контроллеров - которой нет в оригинальной бесплатной версии... поддерживается большое количество микроконтроллеров код стабилен и работает ... я проверял ... у меня два устройства на этом коде висят ...