Gourav kumar
Gourav kumar

Reputation: 33

STM32F4 interface with RS485

I am trying to read data from a meter using a RS485 to TTL Converter to an STM32f407VG. My device slave ID is 121, the baudrate is 9600. I want to read holdingRegisters and InputRegisters.

I am trying this FreeMODBUS RTU port for STM32 HAL library .

I have connected my DI pin to PA_2(Tx), R0 pin to PA_3(Rx), DE&RE pin to GND. But I am not getting any data.

This is my code:

#include "stm32f4xx_hal.h"
#include "cmsis_os.h"

#include "mb.h"
#include "mbport.h"

#define REG_INPUT_START 30005
#define REG_INPUT_NREGS 8

static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];

 void ModbusRTUTask(void const * argument)  
 { 
    /* ABCDEF */
   usRegInputBuf[0] = 11;
    usRegInputBuf[1] = 22;
    usRegInputBuf[2] = 33;
    usRegInputBuf[3] = 44;
    usRegInputBuf[4] = 55;
    usRegInputBuf[5] = 66;
    usRegInputBuf[6] = 77;
    usRegInputBuf[7] = 88;  

  eMBErrorCode eStatus = eMBInit( MB_RTU, 121, 3, 9600, MB_PAR_NONE );
  eStatus = eMBEnable();


while(1) {
    eMBPoll();           
  }
 }

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode    eStatus = MB_ENOERR;
int             iRegIndex;

if( ( usAddress >= REG_INPUT_START )
   && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
   iRegIndex = ( int )( usAddress - usRegInputStart );
   while( usNRegs > 0 )
            {
      *pucRegBuffer++ =
         ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
     *pucRegBuffer++ =
        ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
       iRegIndex++;
       usNRegs--;
    }

        HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
    }
    else
    {
         HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin);
    eStatus = MB_ENOREG;            
    }

    return eStatus;
    }

    eMBErrorCode
    eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
             eMBRegisterMode eMode )
    {
    return MB_ENOREG;
     }


    eMBErrorCode
    eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
           eMBRegisterMode eMode )
    {
    return MB_ENOREG;
    }

      eMBErrorCode
       eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT 
        usNDiscrete)
       {
       return MB_ENOREG;
      }

Why these variables ?

usRegInputBuf[0] = 11;
usRegInputBuf[1] = 22;
usRegInputBuf[2] = 33;
usRegInputBuf[3] = 44;
usRegInputBuf[4] = 55;
usRegInputBuf[5] = 66;
usRegInputBuf[6] = 77;
usRegInputBuf[7] = 88;  

What changes do I need to make?

Upvotes: 2

Views: 14051

Answers (3)

Renee Cousins
Renee Cousins

Reputation: 165

@kamilcuk is close, but on all RS485 transceivers, the DE is active high and the RE is active low. Tying DE and RE low will create a universal receiver that cannot send.

The simplest "half-duplex" RS485 is to connect RE and DE to a single GPIO pin. Drive this signal HIGH (GPIO_PIN_SET in HAL lingo) to transmit and set the signal LOW (GPIO_PIN_RESET in HAL lingo) to receive.

If you have RE and DE attached to different GPIO pins, that's okay, just set them to the same value at the same time. I use a couple of macros to do this, so I don't have to always think about the logic.

Then just name the pins accordingly in the CubeMX editor.

// RS485_DE Data Enable, Active High
// RS485_RE Receive En, Active Low
#define ENABLE_TRANSMIT() do { \
    /* Disable Receiver */ \
    HAL_GPIO_WritePin(RS485_RE_GPIO_Port, RS485_RE_Pin, GPIO_PIN_SET); \
    /* Enable Transmitter */ \
    HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); \
    } while(0)

#define ENABLE_RECEIVE() do { \
    /* Enable Receiver */ \
    HAL_GPIO_WritePin(RS485_RE_GPIO_Port, RS485_RE_Pin, GPIO_PIN_RESET); \
    /* Disable Transmitter */ \
    HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); \
    } while(0)

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141000

DE&RE pin to GND

DE and RE pins are used to enable and disable input and output from the converted. You should:

  • set DE=1 and RE=0 when transmitting
  • set DE=0 and RE=1 when receiving
  • (extra) set DE=0 and RE=0 when not using the device (this is dependent on the transceiver, but usually setting both to low saves a lot of energy)

Before sending each character through rs485, you need to set DE=1, RE=0, then write the character, then change back to receive mode and then receive the data.

Inspect the datasheet of the transceiver very carefully. If you use, ex. MAX485 (but really, their're all the same usuallly), you see at page 7:

enter image description here

More about three state logic can be found ex. on wiki.

With RE set to GND the gates will never open, so you will have always high impedance on RO pin and never receive any data. Other way round, you will never send any data, because DE will be low, and I believe you need to send smth to the meter before it starts transmitting.

You should connect uart TX to DI, uart RX to RO. In your case and if the converter inverts one of the RE/DE pins (usually RE is inverted, as above) you can connect both to the same pin.

Upvotes: 3

KaNkAn Sarkar
KaNkAn Sarkar

Reputation: 16

The Github code is messy, i would suggest you to follow the UART communication procedure mentioned here

  1. First check using a serial terminal if you are able to communicate using UART with the stm32f4x board.
  2. Use modbus communication protocol to implement your register reading logic.

Upvotes: 0

Related Questions