Vilket MODBUS bibliotek använder du?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Protokollet är en sak. Det är inte så svårt. Men att få ett protokoll fungera utmärkt på hårdvara med hjälp av C kod är en helt annan nivå.

Jo. Det finns en stor anledning att alltid köra DMA. DMA läser meddelandet under körningen, så jag slipper köra det. DMA är alltid rekommenderat.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Nej, DMA är inte alltid bra, eftersom du måste hålla koll på Time-outs om du skall följa protokollet.
Protokollet är en sak. Det är inte så svårt. Men att få ett protokoll fungera utmärkt på hårdvara med hjälp av C kod är en helt annan nivå.
Nja, det tycker jag inte.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Det är väl interruptstyrning som gör att man kan ta emot i bakgrunden.
Ta alla dessa 3d-skrivare som tar emot serie-data, de har en isr som går när uarten tagit emot en byte och stoppar undan byten där den ska vara.
För bara några år sedan gick alla de på 8-bitars processorer, fanns inte DMA.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46872
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: Vilket MODBUS bibliotek använder du?

Inlägg av TomasL »

Eftersom man bara skall ta emot ett fåtal bytes, och de flesta prollar har en hårdvarubuffer går det utmärkt att köra med interrupt.

min RX-rutin som ligger i ett Serie-interrup ser ut så här (dock inte hela funktionen, då TX ligger i samma interrupt

Kod: Markera allt

 if( mU2RXGetIntFlag() )         
   {
                    
   
    if (( U2STAbits.FERR)|| ( U2STAbits.OERR) ||( U2STAbits.PERR))
      {     
			if (U2STAbits.FERR)
			{
					global_data.MB_counter_stat.FRERR_u32++;
			}

			if (U2STAbits.OERR)
			{
					global_data.MB_counter_stat.OWERR_u32++;
			}
			
			if (U2STAbits.PERR)
			{
					global_data.MB_counter_stat.PAERR_u32++;
			}

        bRxFailFlag =TRUE;
        
       //MCU PIC32MX360F512L has only 4-bytes deep FIFO
        ubRxByte = U2RXREG;   // Flush register
        ubRxByte1 = U2RXREG;   // Flush register
        ubRxByte2 = U2RXREG;   // Flush register
        ubRxByte3 = U2RXREG;   // Flush register         
      
        ubRxByte4 = U2RXREG;   // Flush register
        ubRxByte5 = U2RXREG;   // Flush register 
        ubRxByte6 = U2RXREG;   // Flush register 
        ubRxByte7 = U2RXREG;   // Flush register 
//        test232_Puts("\nUART-RESET\n");
      
       
          if( NULL != xSerialHdls[1].pvMBPReceiveFN )
          {
	               //MCU PIC32MX360F512L has only 4-bytes deep FIFO//
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte1 );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte2 );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte3 );
                     
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte4 );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte5 );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte6 );
                  xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte7 );
                 
          }
        // clean Overflow error if presents
        if (U2STAbits.OERR)
        {
     
     
	        
            U2STACLR=_U2STA_OERR_MASK;
            
        }
      }
      
      if ( bRxFailFlag== FALSE)
      {             
			while(U2STAbits.URXDA == 1)
			{
				// Retrieve the received character and place it in ubRxByte //      
				
				ubRxByte = U2RXREG;
				
				eMBRCVDbg[eMBRCVDbgCount] = ubRxByte;
				
				eMBRCVDbgCount++;
				eMBRCVDbgCount &= 1023;        
				
			   PORTToggleBits(RS485_MB_RXLED_PORT, RS485_MB_RXLED_MASK);
	
				/*utility_i2hex( pszprnt, ubRxByte, 2);  // DEBUG
				test232_Puts( pszprnt ); 					// DEBUG
				test232_Put( 32 );                		// DEBUG */
				
				if( NULL != xSerialHdls[1].pvMBPReceiveFN )
				{
					xSerialHdls[1].pvMBPReceiveFN( xSerialHdls[1].xMBMHdl, ubRxByte );
				}
                   
			}           
      }
	
		mU2RXClearIntFlag();
      IFS1CLR=_IFS1_U2EIF_MASK; // Clean possible errors  
             
   }
    } 
pfyra
Inlägg: 375
Blev medlem: 8 mars 2015, 14:14:44
Ort: utanför Karlstad

Re: Vilket MODBUS bibliotek använder du?

Inlägg av pfyra »

Man kan sätta upp en dma transfer från uart till buffern som ger en interrupt när antingen buffern är full eller att det har varit tyst på uarten en viss tid. Det blir mycket enklare med en sån uppsättning än med en cirkulär buffer tycker jag.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Jag ska testa använda libmodbus. Typ modifiera libmodbus så den kan använda vilken hårdvara som helst. Normalt så är den bara anpassad efter en viss typ av hårdvara, tyvärr.

Jag kommer inte vidare med nanoMODBUS. Jag förstår koden och dess syfte. Men jag förstår inte samtliga ++ och -- lite här och där för att styra ett index hos en array.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Tittade på ett par av nanoModbus inkluderade exempel. Det är väl bara att kopiera och modifiera efter behov. Vad var det som inte var bra?
Man får polla efter data, men åtminstone de jag tittade på använder serie-rutiner som är buffrade med många byte.
Användarvisningsbild
Icecap
Inlägg: 26621
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Vilket MODBUS bibliotek använder du?

Inlägg av Icecap »

Om man inte kör mycket höga hastigheter på UART'en bör en simpel interrupt-funktion fungera synnerligt väl.

Jag har alltid kört seriell RX medelst interrupt, ändå sedan PIC16Cxxx.

Numera har jag en cirkulär buffer till TX och en vanlig rak buffer till RX, båda betjänas medelst interrupt.
Detta gör det enkelt att ha en timer-interrupt inblandad för att fånga time-out osv.
Detta har jag löst med att ha en variabel för att räkna tiden, en timer-funktion som räknar ner så länge den är >0 - och når RX-interrupt kommer återladdas den variabel till time-out värdet.

Om timer-interrupten ska räkna ner och variabeln redan är 0 har det skett en time-out - och då gör man det som ska göras, i mitt fall oftast spolar RX-buffer o nollställer de pekare som har med den att göra.

Att köra via DMA anser jag blir krångligare och ger inte bättre funktion.

Just nu använder vi en U(S)ART på den STM32-projekt jag jobbar med, den kör 230400N81 - och RX är interrupt-styrd medan TX kör med DMA.

Har debuggat det projekt MYCKET och sett hur mycket som sker för en enkel interrupt - men även om µCn har rikligt att göra är det ett piss i havet för den.

Då jag använde MODBUS till att avläsa data från en sol-inverter, gjorde jag allt från botten.

Den körde RS485 - och det är ju i grunden en ganska vanlig seriell bus så jag jobbade med U(S)ART som vid RS232.

Jag insåg att jag inte kan förlita mig på att "min" del av systemet har en specifik endian - så ALLA data som kommer avläsas via en simpel rutin som läser byte för byte och ger rätt formaterat 16 eller 32 bit värden.
På det vis kommer den källkod att fungera i alla system som jag kan tänkas att använda det i, oavsett den aktuella µC's endian.

Jag är medveten om att TS kan ha svårt att överskåda flow'et i systemet och därför kan behöva andras lösningar - men i MINA ögon är det inte speciellt vettigt att ta in andras lösningar för att lösa en ganska trivial uppgift.

Jag är å andra sidan också "en aning" noga med att undvika buffer overflow, kör MISRA bäst det går och är generellt EXTREMT skeptisk till funktioner jag inte har skrivit själva.

Alla kan skriva mjukvara som fungerar när solen skiner o man har en glass i handen - men vad när (metaforisk) underkyld regn kommer utav bara fan och ölen är pissljummen? Fungerar systemet då?
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Jag har tittat på många av deras exempel. Deras STM32 exempel kräver RTOS. Men det finns ett exempel för Arduino som jag försöker efterlikna.
Men jag börjar se problemet nu med nanoMODBUS.

Jag använder CAS för att testa meddelanden. Tydligen så verkar det så att ibland fungerar det, ibland fungerar det inte.
Skärmbild 2024-12-29 123703.png
Jag tror det har med denna kod. Och det som orsakar är den där if-satsen som jämför current_index med end_index som är problemet.

Kod: Markera allt

int32_t UART_read(const char port[], uint8_t* buf, uint16_t count, int32_t byte_timeout_ms) {
	/* Get the end index */
	uint16_t end_index = (UART_RX_DATA_SIZE - hdma_usart1_rx.Instance->CNDTR);

	/* If current index is the same as end index, then no data has been received */
	if(current_index == end_index){
		return 0;
	}

	/* If current index and the count data is less or equal as the total data size buffer */
	if(current_index + count <= UART_RX_DATA_SIZE){
		memcpy(buf, UART_RX_DATA + current_index, count);
		current_index += count;
		return count;
	}

	/* If current index and the count data is more than the total data size buffer */
	if(current_index + count > UART_RX_DATA_SIZE){
		uint16_t count_right = UART_RX_DATA_SIZE - current_index;
		memcpy(buf, UART_RX_DATA + current_index, count_right);
		uint16_t count_left = count - count_right;
		memcpy(buf, UART_RX_DATA, count_left);
		current_index = count_left;
		return count;
	}

	/* This should not occur */
	return 0;
}
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Vad är det för kod du har där? Det hör inte till nanoModbus i alla fall.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Det är ett extra lager för nanoMODBUS.
Det jag gillar med nanoMODBUS är att den täcker hela modbus-protokollet och är lätt att använda. Men det är något i kommunikationen som gör så den tror att den ska skicka via "broadcast".

Vill du ha koden? Den är väldigt enkel att ställa upp. Om du har någon Arduino eller STM32 Nucleo i närheten och en USB till seriell till hands.
hawkan
Inlägg: 3382
Blev medlem: 14 augusti 2011, 10:27:40

Re: Vilket MODBUS bibliotek använder du?

Inlägg av hawkan »

Nä jag har klonat github-koden. Och jag hittar inte UART_read där, så är det något du har gjort?
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Här laddar jag upp min kod.
modbus.zip
Det du behöver göra är att använda dig av dessa funktioner (om du använder STM32, annars kan du använda Arduinos Serial.read(buf, size)).

Kod: Markera allt

#include "modbus/modbus.h"

int32_t UART_read(const char port[], uint8_t* buf, uint16_t count, int32_t byte_timeout_ms) {
	/* Get the end index */
	uint16_t end_index = (UART_RX_DATA_SIZE - hdma_usart1_rx.Instance->CNDTR);

	/* If current index is the same as end index, then no data has been received */
	if(current_index == end_index){
		return 0;
	}

	/* If current index and the count data is less or equal as the total data size buffer */
	if(current_index + count <= UART_RX_DATA_SIZE){
		memcpy(buf, UART_RX_DATA + current_index, count);
		current_index += count;
		return count;
	}

	/* If current index and the count data is more than the total data size buffer */
	if(current_index + count > UART_RX_DATA_SIZE){
		uint16_t count_right = UART_RX_DATA_SIZE - current_index;
		memcpy(buf, UART_RX_DATA + current_index, count_right);
		uint16_t count_left = count - count_right;
		memcpy(buf, UART_RX_DATA, count_left);
		current_index = count_left;
		return count;
	}

	/* This should not occur */
	return 0;
}

int32_t UART_write(const char port[], const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms) {
	HAL_StatusTypeDef status = HAL_UART_Transmit(&huart1, buf, count, byte_timeout_ms);

	const bool wrote = status == HAL_OK;
    if(wrote){
    	return count; /* Assuming that we wrote count bytes */
    }else{
    	return 0;
    }
}

För att konfigurera så starta upp modbus så här:

Kod: Markera allt

  modbus_set_serial_read(UART_read);
  modbus_set_serial_write(UART_write);
  modbus_set_serial_port(NULL);
  modbus_server_create_RTU(1);
och i din while-loop, anropa denna

Kod: Markera allt

modbus_server_polling();
Sedan använder du CAS modbus scanner. Nu kan du reproducera felet som jag får. Vad använder du för plattform?
https://store.chipkin.com/products/tool ... us-scanner
Skärmbild 2024-12-29 143057.png
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Lite kort hur detta bibliotek fungerar.
Steg 1:
Först anropas denna funktion

Kod: Markera allt

modbus_server_polling();
Koden som körs är denna. Vad denna gör är att den ska läsa ett meddelande för att kunna ta ett beslut.

Kod: Markera allt

bool modbus_polling(){
	if(nmbs_server){
		nmbs_error err = nmbs_server_poll(nmbs_server);
		if (err != NMBS_ERROR_NONE) {
			return false;
		}
		return true;
	}
	return false;
}
Steg 2:
Denna kod är själva polling-funktionen. Den läser och sedan skriver den

Kod: Markera allt

nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
    msg_state_reset(nmbs);

    bool first_byte_received = false;
    nmbs_error err = recv_req_header(nmbs, &first_byte_received);
    if (err != NMBS_ERROR_NONE) {
        if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
            return NMBS_ERROR_NONE;

        return err;
    }

#ifdef NMBS_DEBUG
    printf("%d ", nmbs->address_rtu);
    printf("NMBS req <- ");
    if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
        if (nmbs->msg.broadcast)
            printf("broadcast\t");
        else
            printf("address_rtu %d\t", nmbs->msg.unit_id);
    }
#endif

    err = handle_req_fc(nmbs);
    if (err != NMBS_ERROR_NONE) {
        if (err != NMBS_ERROR_TIMEOUT)
            flush(nmbs);

        return err;
    }

    return NMBS_ERROR_NONE;
}
Steg 3:
Låt oss börja först med läsningen. Den ska alltså läsa en header. Troligtvis är det en "data-frame". Vi går vidare in till funktionen recv_msg_header.

Kod: Markera allt

static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
    nmbs_error err = recv_msg_header(nmbs, first_byte_received);
    if (err != NMBS_ERROR_NONE)
        return err;

    if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
        // Check if request is for us
        if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
            nmbs->msg.broadcast = true;
        else if (nmbs->msg.unit_id != nmbs->address_rtu)
            nmbs->msg.ignored = true;
        else
            nmbs->msg.ignored = false;
    }

    return NMBS_ERROR_NONE;
}
Steg 4:
Här är koden för att läsa. Det denna kod gör är att den läser ID och FUNKTION i dataramen. Alltså två byte. Den är något försiktig dock när den läser då den läser via get_1 som betyder "get one byte". För varje "byte" den har läst, så "hoppar" den ett steg. Funktionen recv(nmbs, 1); anropar alltså den där UART funktionen som du har ovan med storleken size = 1.

Kod: Markera allt

static nmbs_error recv_msg_header(nmbs_t* nmbs, bool* first_byte_received) {
    // We wait for the read timeout here, just for the first message byte
    int32_t old_byte_timeout = nmbs->byte_timeout_ms;
    nmbs->byte_timeout_ms = nmbs->read_timeout_ms;

    msg_state_reset(nmbs);

    *first_byte_received = false;

    if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
        nmbs_error err = recv(nmbs, 1);

        nmbs->byte_timeout_ms = old_byte_timeout;

        if (err != NMBS_ERROR_NONE)
            return err;

        *first_byte_received = true;

        nmbs->msg.unit_id = get_1(nmbs);

        err = recv(nmbs, 1);
        if (err != NMBS_ERROR_NONE)
            return err;

        nmbs->msg.fc = get_1(nmbs);
    }
    else if (nmbs->platform.transport == NMBS_TRANSPORT_TCP) {
       Denna kod är bortklippt för den visar bara TCP-IP.
    }

    return NMBS_ERROR_NONE;
}
Steg 5:
Efter vi har fått våra ID byte och FUNKTION byte. En sak förstår jag inte varför nmbs->msg.unit_id har alltid en fixerad broadcast adress på 0 så fort vi läser ett meddelande.
Då är vill tillbaka till denna kod igen

Kod: Markera allt

static nmbs_error recv_req_header(nmbs_t* nmbs, bool* first_byte_received) {
    nmbs_error err = recv_msg_header(nmbs, first_byte_received);
    if (err != NMBS_ERROR_NONE)
        return err;

    if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
        // Check if request is for us
        if (nmbs->msg.unit_id == NMBS_BROADCAST_ADDRESS)
            nmbs->msg.broadcast = true;
        else if (nmbs->msg.unit_id != nmbs->address_rtu)
            nmbs->msg.ignored = true;
        else
            nmbs->msg.ignored = false;
    }

    return NMBS_ERROR_NONE;
}
Steg 6:
Vi är tillbaka till denna kod igen och nu ska vi läsa våran funktionskod. Detta gör via via handle_req_fc(nmbs);

Kod: Markera allt

nmbs_error nmbs_server_poll(nmbs_t* nmbs) {
    msg_state_reset(nmbs);

    bool first_byte_received = false;
    nmbs_error err = recv_req_header(nmbs, &first_byte_received);
    if (err != NMBS_ERROR_NONE) {
        if (!first_byte_received && err == NMBS_ERROR_TIMEOUT)
            return NMBS_ERROR_NONE;

        return err;
    }

#ifdef NMBS_DEBUG
    printf("%d ", nmbs->address_rtu);
    printf("NMBS req <- ");
    if (nmbs->platform.transport == NMBS_TRANSPORT_RTU) {
        if (nmbs->msg.broadcast)
            printf("broadcast\t");
        else
            printf("address_rtu %d\t", nmbs->msg.unit_id);
    }
#endif

    err = handle_req_fc(nmbs);
    if (err != NMBS_ERROR_NONE) {
        if (err != NMBS_ERROR_TIMEOUT)
            flush(nmbs);

        return err;
    }

    return NMBS_ERROR_NONE;
}
Steg 7:
Nu ska vi avgöra vad vi ska ta för beslut. Om vi har FUNKTION = 4 så betyder det att vi ska skicka ett register. Då anropar vi err = handle_read_input_registers(nmbs);

Kod: Markera allt

static nmbs_error handle_req_fc(nmbs_t* nmbs) {
    NMBS_DEBUG_PRINT("fc %d\t", nmbs->msg.fc);

    nmbs_error err = NMBS_ERROR_NONE;
    switch (nmbs->msg.fc) {
#ifndef NMBS_SERVER_READ_COILS_DISABLED
        case 1:
            err = handle_read_coils(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_DISCRETE_INPUTS_DISABLED
        case 2:
            err = handle_read_discrete_inputs(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED
        case 3:
            err = handle_read_holding_registers(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED
        case 4:
            err = handle_read_input_registers(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_WRITE_SINGLE_COIL_DISABLED
        case 5:
            err = handle_write_single_coil(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_WRITE_SINGLE_REGISTER_DISABLED
        case 6:
            err = handle_write_single_register(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_WRITE_MULTIPLE_COILS_DISABLED
        case 15:
            err = handle_write_multiple_coils(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_WRITE_MULTIPLE_REGISTERS_DISABLED
        case 16:
            err = handle_write_multiple_registers(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_FILE_RECORD_DISABLED
        case 20:
            err = handle_read_file_record(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_WRITE_FILE_RECORD_DISABLED
        case 21:
            err = handle_write_file_record(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_WRITE_REGISTERS_DISABLED
        case 23:
            err = handle_read_write_registers(nmbs);
            break;
#endif

#ifndef NMBS_SERVER_READ_DEVICE_IDENTIFICATION_DISABLED
        case 43:
            err = handle_read_device_identification(nmbs);
            break;
#endif
        default:
            flush(nmbs);
            if (!nmbs->msg.ignored && !nmbs->msg.broadcast)
                err = send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
    }

    return err;
}
Steg 8:
Här är liksom slutpunkten för meddelandet. Om inte if (!nmbs->msg.broadcast) kör så blir det en timeout.

Kod: Markera allt

#if !defined(NMBS_SERVER_READ_HOLDING_REGISTERS_DISABLED) || !defined(NMBS_SERVER_READ_INPUT_REGISTERS_DISABLED)
static nmbs_error handle_read_registers(nmbs_t* nmbs,
                                        nmbs_error (*callback)(uint16_t, uint16_t, uint16_t*, uint8_t, void*)) {
    nmbs_error err = recv(nmbs, 4);
    if (err != NMBS_ERROR_NONE)
        return err;

    uint16_t address = get_2(nmbs);
    uint16_t quantity = get_2(nmbs);

    NMBS_DEBUG_PRINT("a %d\tq %d", address, quantity);

    err = recv_msg_footer(nmbs);
    if (err != NMBS_ERROR_NONE)
        return err;

    if (!nmbs->msg.ignored) {
        if (quantity < 1 || quantity > 125)
            return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_VALUE);

        if ((uint32_t) address + (uint32_t) quantity > ((uint32_t) 0xFFFF) + 1)
            return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_DATA_ADDRESS);

        if (callback) {
            uint16_t regs[125] = {0};
            err = callback(address, quantity, regs, nmbs->msg.unit_id, nmbs->callbacks.arg);
            if (err != NMBS_ERROR_NONE) {
                if (nmbs_error_is_exception(err))
                    return send_exception_msg(nmbs, err);

                return send_exception_msg(nmbs, NMBS_EXCEPTION_SERVER_DEVICE_FAILURE);
            }

            // TODO check all these read request broadcast use cases
            if (!nmbs->msg.broadcast) {
                uint8_t regs_bytes = quantity * 2;
                put_res_header(nmbs, 1 + regs_bytes);

                put_1(nmbs, regs_bytes);

                NMBS_DEBUG_PRINT("b %d\t", regs_bytes);

                NMBS_DEBUG_PRINT("regs ");
                for (int i = 0; i < quantity; i++) {
                    put_2(nmbs, regs[i]);
                    NMBS_DEBUG_PRINT("%d ", regs[i]);
                }

                err = send_msg(nmbs);
                if (err != NMBS_ERROR_NONE)
                    return err;
            }else{
            	int a = 0; /* Broroad cast! */
            }
        }
        else {
            return send_exception_msg(nmbs, NMBS_EXCEPTION_ILLEGAL_FUNCTION);
        }
    }
    else {
        return recv_read_registers_res(nmbs, quantity, NULL);
    }

    return NMBS_ERROR_NONE;
}
DanielM
Inlägg: 2415
Blev medlem: 5 september 2019, 14:19:58

Re: Vilket MODBUS bibliotek använder du?

Inlägg av DanielM »

Nu har jag löst problemet. En lite fördröjning skadar dock inte. Det kanske inte är så att DMA är blixtsnabbt som jag hade förväntat mig.

Kod: Markera allt

int32_t UART_read(const char port[], uint8_t* buf, uint16_t count, int32_t byte_timeout_ms) {
	/* Get the end index */
	uint16_t end_index = (UART_RX_DATA_SIZE - hdma_usart1_rx.Instance->CNDTR);

	/* If current index is the same as end index, then no data has been received */
	if(current_index == end_index){
		return 0;
	}

	/* Important delay for DMA loading */
	HAL_Delay(10);

	/* If current index and the count data is less or equal as the total data size buffer */
	if(current_index + count <= UART_RX_DATA_SIZE){
		memcpy(buf, UART_RX_DATA + current_index, count);
		current_index += count;
		return count;
	}

	/* If current index and the count data is more than the total data size buffer */
	if(current_index + count > UART_RX_DATA_SIZE){
		uint16_t count_right = UART_RX_DATA_SIZE - current_index;
		memcpy(buf, UART_RX_DATA + current_index, count_right);
		uint16_t count_left = count - count_right;
		memcpy(buf, UART_RX_DATA, count_left);
		current_index = count_left;
		return count;
	}

	/* This should not occur */
	return 0;
}
Senast redigerad av DanielM 30 december 2024, 11:18:49, redigerad totalt 2 gånger.
Skriv svar