I am new to modbus and I have to program a lpcxpresso baseboard as a master to collect readings from a powermeter using RS485 Modbus protocol.
I am familiar with the protocol (about the PDU ADU frame, function codes, master-slave) through reading of specifications from modbus.org.
However I have difficulties in the implementation when writing the code in C.
So my questions are:
Do I have to open connection, set the baud rate, etc when I am starting the connection?
I am thinking to send the frame as byte[]. Is this correct? or are there any other ways to do it?
Does the data send have to be in hexadecimal, or binary or integer?
CRC generation and checking.
I will really appreciate all kind of help and assistance :) Sorry if the questions are not very specific or too basic
Step 1: Forget about energy meter and modbus for now. Most important thing is to get hardware working. RS485 is simply a serial port. Read manual on how to initialize serial port on your hardware, and send single byte to your PC and back. Then send hundreds of bytes to PC and back.
Setp 2: Get timer on your hardware working also. Modbus protocol has some requirements on timing so you'll need it too.
Step 3: Get modbus specification. It will explain protocol format and checksums. Use modbus library or write your own. Make sure you can make it work with PC, before you move on to the energy meter.
Step 4: If you have a problem, ask specific question about it on SO.
First of all: is ModBus RTu or ASCII?
Yes, of course. You need to specify all things as specs describe.
Yes, it is a unsigned char[]. The structure is described by specs.
The question doesn't make sense: You always send info as "memory
dump", but with RTU you send 1 byte per memory byte, in case of
ASCII you send 2 byte per memory byte. Eg. if you have to send a
byte 0xAE: RTU=0xAE - ASCII= 0x41 0x45. In case of RTU, if you have to send an int (4 byte) you will send those bytes as they are stored in memory, eg: 12345 will be sent as 0x00 0x00 0x30 0x39 (big endian), 0x39 0x30 0x00 0x00 (little endian).
The calculation of CRC is explained in specs. Below the code of my old CBuilder component
unsigned short TLPsComPort::Calculate_CRC16 ( int Message_Length, char *Message
{
char Low_CRC;
char Bit;
// Constant of ModBus protocol
unsigned short CONSTANT = 0xA001;
unsigned short CRC_REGISTER = 0xFFFF;
for (int i=0; i<Message_Length; i++)
{
Low_CRC = CRC_REGISTER;
Low_CRC = *(Message+i) ^ Low_CRC;
CRC_REGISTER = ((CRC_REGISTER & 0xFF00) | (Low_CRC & 0x00FF));
for (int j=0; j<8;j++)
{
Bit = CRC_REGISTER & 0x0001;
CRC_REGISTER = (CRC_REGISTER >> 1) & 0x7FFF;
if (Bit) CRC_REGISTER = CRC_REGISTER ^ CONSTANT;
}
}
return CRC_REGISTER;
}
Related
I am trying to initialize an SD card using an SPI bus and STM32F4 Discovery Board. I am mainly relying on Elm Chan's implementation of the disk_initialize function in the example code to base my own implementation. Unfortunately, I have run into an issue where sending CMD58 to the SD card during the initialization process return a result of 0x01, which implies that the SD card is idle. However, I am still seeing the next four bytes from the SD card as 0x00, 0xFF, 0x80, 0x00 which is in the right format for an R3 response. However, I am not sure if I can trust these four bytes as my OCR.
As of now, I have tried ignoring that the SD card is idling and simply tried to use the next four bytes as the OCR but the code seems to fail at other points during the mounting process with respect to the type of the card being assumed from the OCR.
if (Timer1 && SD_SendCmd(CMD58, 0) == 0) {
for (n = 0; n < 4; n++) {
ocr[n] = SPI_RxByte();
}
type = (ocr[0] & 0x40) ? 6 : 2;
}
The code segment above is where I am first seeing the idle response. SD_SendCmd is where I send CMD58 to the SD card and where I am receiving 0x01 as the leftmost byte of the five byte response. Because I am not receiving 0x00, which signals that the SD card has no issues with the command passed to it, the code breaks out of the initialization process and returns an error. I would greatly appreciate any help with this matter as I have been stumped by this 0x01 return value for quite some time now. Thank you!
So I was able to figure out the issue. It turns out that the card I was using was an SDHC card, where HC stands for high capacity. According to the simplified SD card specification, the CRC, which is sent at the end of the transmission of a command has to have the least significant bit set to 1. So, ORing the CRC with 0x01 before any of the transmissions let me initialize and use any type of SD card. So the issue I was having was not from CMD58, but how I was dealing with the CRC in general. Interestingly enough, not ORing the CRC seems to work fine with non high-capacity SD cards. But ORing the CRC with 0x01 seems to work with all cards (at least as far as I have tested).
With regard to ORing the CRC with 0x01, this is not the CRC, but the STOP-BIT, the CRC is of type CRC7 and is at bits 1:7 of byte #6 in the command. According to the specification, even if the CRC is not required, the stop bit must always be 1.
I'm a begginer in programming AVR microcontroler and I get a lot of headacke sometimes from reading the datasheets.
I'm trying to make a communication between my AVR and PC just to send some caracters and receive it on my computer.
There are two lines I don't understand from the whole program and that is:
void USART_init(void)
{
UBRRH = (uint8_t)(BAUD_PRESCALLER>>8); <---- this one!
UBRRL = (uint8_t)(BAUD_PRESCALLER); <--- and this one
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<UCSZ0)|(1<<UCSZ1)|(1<<URSEL);
}
Datasheet
Why do I have to shift BAUD_PRESCALLER with 8? If BAUD_PRESCALLER is a number and shifting that number with 8 doesn't mean the result will be zero?(Because we are shifting it too many times)
From the datasheet I understand that UBRRH contains the four most significant bits and the UBRRL contains the eight least signicant bits of the USART baut rate.(Note:UBBR is a 12-bit register)
So how actually we put all the required numbers in the UBBR register?
You have to shift it right 8 bits because the result of BAUD_PRESCALLER is larger than 8 bits. Shifting it right 8 bits gives you the most significant byte of a 16-bit value.
For example, if the value of BAUD_PRESCALAR is 0x123 - then 0x1 would be assigned to UBRRH and 0x23 would be assigned to UBRRL.
If the library was smart it could also perform sanity checking on the BAUD_PRESCALAR to make sure it fits in 16bits. If it can't, that means you cannot achieve the baud rate you want given the clock you are using. If you're UBRRx is truly 12bits, the sanity check would look something like this:
#if BAUD_PRESCALAR > 0xFFF
#error Invalid prescalar
#endif
I am trying to read the data from FXLS8471Q 3-Axis, Linear Accelerometer using SPI. I am using bit banging method to read the data from Accelerometer. I am using LPC 2184 ARM processor. I used the following code.
unsigned char spiReadReg (const unsigned char regAddr)
{
unsigned char SPICount;
unsigned char SPIData;
SPI_CS = 1;
SPI_CK = 0;
SPIData = regAddr;
SPI_CS = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
if (SPIData & 0x80)
SPI_MOSI = 1;
else
SPI_MOSI = 0;
SPI_CK = 1;
SPI_CK = 0;
SPIData <<= 1;
}
SPI_MOSI = 0;
SPIData = 0;
for (SPICount = 0; SPICount < 8; SPICount++)
{
SPIData <<=1;
SPI_CK = 1;
SPIData += SPI_MISO;
SPI_CK = 0;
SPIData &=(0xFE);
}
SPI_CS = 1;
return ((unsigned char)SPIData);
}
But instead of getting valid value 0x6A , I am getting garbage value.
Please help me out to solve this problem;
SPIData &=(0xFE); as pointed out in another answer, is definitely wrong as it erases the bit you just received.
However, there are other major issues with your code.
An SPI slave device sends you data by setting the value of MISO on a rising or falling clock, depending on the type of device. However, you didn't wait in your code for the value to appear on MISO.
You control the communication by setting the clock to 1 and 0. The datasheet of the accelerometer says on page 19, that
Data is sampled during the rising edge of SCLK and set up during the falling edge of SCLK.
This means that in order to read from it, your processor needs to change the clock from one to zero, thereby signaling the accelerometer to send the next bit to the MISO. This means you did the reverse, you in your code read on a rising edge while you should be reading on the falling edge. After setting the clock to zero, you have to wait a little while until the value appears on MISO, and only then should you read it and add it to your SPIData variable. Table 9, SPI timing indicates how much you have to wait: at least 500 nanoseconds. That's not much, but if your CPU runs faster than 2 MHz then if you don't use a delay, you will try to read the MISO before the accelerometer had time to properly set it.
You also forgot the slave Select, which is actually required to indicate the beginning and the end of a datagram.
Check the Figure 7. SPI Timing Diagram in the datasheet, it indicates what you are required to do and in what order, to communicate with the device using SPI.
I also suggest reading about how the rotating registers of the SPI work, because it seems from its datasheet, that the accelerometer needs to receive a well specified number of bits before useful data appears on its output. Don't forget, as you send a single bit to the device, it also has to send a bit back to you, so if it didn't decode a command yet, it can only send gibberish. Your code, as the master, can only "push" bits in, and collect the bits which "pop out" on the other side. This means you have to send a command, and then send further bits until all the answer is pushed out to you bit by bit.
If you get stuck, I think you will have much more luck getting help on https://electronics.stackexchange.com/, but instead of just putting this same code there (which seems to be blindly copied from www.maximintegrated.com and has absolutely nothing to do with the problem you try to solve), I strongly recommend trying to understand the "Figure 7. SPI Timing Diagram" I was suggesting before, and alter your code accordingly.
Without understanding how the device you try to communicate with works, you will never succeed if you just blindly copy the code from a completely different project just because it says "spi" in the title.
SPIData &=(0xFE);
The above line is causing the problem. Here the LSB is reset to 0 (which contained the data bit just taken from MISO) -- basically you are destroying the bit you just read. Omitting the line should correct the problem.
be sure to compile your function with NO optimization
as that will corrupt the bitbang operation.
this is the code from the maxum site for the spiReadReg function.
(which looks like were you got your code.
However, this is just a guide for the 'general' sequence of operations for communicating with the maxim 1481 part.
the accel. part needs several setup commands and reads completely differently
Suggest reading the app notes and white papers available at freescale.com for the specific part number.
Those app notes/white papers will indicate the sequence of commands needed for setting up a specific mode of operation and how to request/interpret the resulting data.
There are a number of device specifics that your code has not taken into account.
Per the spec sheet the first bit transmitted is a read/write indicator, followed by 8 bits of register address, followed by 7 trash bits (suggest sending all 0's for the trash bits.) followed by the data bits.
Depending on the setup commands, those data bits could be 8 bits or 14 bits or multiple registers of 8 or 14 bits per register.
I'm working on a project on which I need to generate the OSPF packet manually. I am currently having problems getting the OSPF checksum right. I read that I have to keep the Auth data out of the calculation, and even though I'm doing that I can't get it to work. I know that the function being used to generate the checksum is correct because I use the same one to generate the checksum for the IP header, and that works.
*I'm sorry for my bad C programming, it's not my main language.
void generateHello(unsigned char* packet_return,unsigned char* buff,unsigned short *ospf_packet){
ospf_packet = (unsigned short*) malloc(14*sizeof(unsigned short));
//OSPF Version
packet_return[34] = 0x02;
//Message Type - Hello
packet_return[35] = 0x01;
//Packet Length
packet_return[36] = 0x00;
packet_return[37] = 0x2c;
//Source OSPF Router (IP)
packet_return[38]=local_ip[0];
packet_return[39]=local_ip[1];
packet_return[40]=local_ip[2];
packet_return[41]=local_ip[3];
//Area
packet_return[42]=0x00;
packet_return[43]=0x00;
packet_return[44]=0x00;
packet_return[45]=0x01;
//ADD CHECKSUM
packet_return[46]=0x00;
packet_return[47]=0x00;
//Auth Type
packet_return[48]=0x00;
packet_return[49]=0x00;
//Auth Data
packet_return[50]=0x00;
packet_return[51]=0x00;
packet_return[52]=0x00;
packet_return[53]=0x00;
packet_return[54]=0x00;
packet_return[55]=0x00;
packet_return[56]=0x00;
packet_return[57]=0x00;
//Network Mask
packet_return[58]=0xff;
packet_return[59]=0xff;
packet_return[60]=0xff;
packet_return[61]=0x00;
//Hello Interval
packet_return[62]=0x00;
packet_return[63]=0x0a;
//Multi-Topology Routing
packet_return[64]=0x12;
//Router Priority
packet_return[65]=0x01;
//Router Dead Interval
packet_return[66]=0x00;
packet_return[67]=0x00;
packet_return[68]=0x00;
packet_return[69]=0x28;
//Designated Router
packet_return[70]=0x00;
packet_return[71]=0x00;
packet_return[72]=0x00;
packet_return[73]=0x00;
//Backup designated router
packet_return[74]=0x00;
packet_return[75]=0x00;
packet_return[76]=0x00;
packet_return[77]=0x00;
//Checksum
packet_return[78]=0x00;
packet_return[79]=0x00;
//LLS Data Length
packet_return[80]=0x00;
packet_return[81]=0x03;
//Type
packet_return[82]=0x00;
packet_return[83]=0x01;
//Length
packet_return[84]=0x00;
packet_return[85]=0x04;
//Options - LSDB Resynchronization
packet_return[86]=0x00;
packet_return[87]=0x00;
packet_return[88]=0x00;
packet_return[89]=0x01;
int i;
int j;
for(i=0,j=34;i<48;i++,j+=2)
{
ospf_packet[i]= htons(((packet_return[j] << 8) | packet_return[j+1]));
}
unsigned short ck_sum = in_cksum(ospf_packet,sizeof(unsigned short)*14);
printf("CHECKSUM OSPF - %.4x \n", ck_sum);
packet_return[46]=ck_sum & 0xff;
packet_return[47]=(ck_sum >> 8) & 0xff;
}
Firstly, please use constants or better a struct with __attribute__((packed)) rather than lots of array offsets.
Secondly, this looks fishy:
for(i=0,j=34;i<48;i++,j+=2)
{
ospf_packet[i]= htons(((packet_return[j] << 8) | packet_return[j+1]));
}
ospf_packet is 14 unsigned shorts long, per the malloc. Yet you are writing 48 unsigned shorts into it. That will cause undefined behaviour.
Also, packet_return appears to be a char * so is presumably in wire order. You are then reading it out assuming it is all shorts in wire order (fine as far as it goes I suppose), then converting it from wire order to host order (it seems) - I think that should be ntohs not htons (yes, I know they do the same thing). It is not evident why you are doing this at all.
Lastly, the OSPF checksum is calculated over the entire OSPF packet except the authentication field.
From RFC2328
Checksum
The standard IP 16-bit one's complement checksum of the
entire OSPF packet, excluding the 64-bit authentication
field. This checksum is calculated as part of the
appropriate authentication procedure; for some OSPF
authentication types, the checksum calculation is omitted.
See Section D.4 for details.
I can't immediately see why your code sums across the entire packet nor how it omits the authentication field.
I'm in to embedded coding and i have a problem here.I'm using uart communication and want to send integers over the serial line.each integer must be shown as character in ascii.What all things should i be careful of?.
The data from controller side will be in integer/char/float format.What should i need to take care of while dealing with displaying those data from controller on a serial monitoring s/w?.
I'm coding in c language
Regards
If what you are looking for is converting integers to ascii there is a standard compliant way to do it.
sprintf(str, "%d", your_integer);
I assume you want to send/receive data with UART communication using micro controller such as 8051.
So here are the things you should be looking at.
1) What is the Baud rate you want to send with? [Baud rate is number of symbols transmitted/received per second]
2) How many bits you want to receive per symbol? [Depends on how many bit micro controller do you use]
Here is the code sample for UART communication to send "STACK OVERFLOW" with 9600 Baud rate with 8 bit data for 8051. [From the book "The 8051 Microcontroller and Embedded Systems"]
#include <reg51.h>
void SerTx(unsigned char);
void main(void){
TMOD=0x20; //use Timer 1, mode 2
TH1=0xFD; //9600 baud rate
SCON=0x50;
TR1=1; //start timer
while (1) {
SerTx(‘S’);SerTx(‘T’);SerTx(‘A’);SerTx(‘C’);SerTx(‘K’);
SerTx(‘’);SerTx(‘O’);SerTx(‘V’);SerTx(‘E’);SerTx(‘R’);
SerTx(‘F’);SerTx(‘L’);SerTx(‘O’);SerTx(‘W’);
}}
void SerTx(unsigned char x){
SBUF=x; //place value in buffer
while (TI==0); //wait until transmitted
TI=0;}
When you declare:
unsigned char array[10] = {'F','E','E','D','B','A','B','E','C','A',};
the compiler puts it into a proper digital representation according to the ASCII table (see it below). So on your device side it is like 0x46 for 'F', 0x45 for 'E' and etc. When you send it through the serial by passing by this array to TX function it will go to the other side with same codes, again like 0x46 for 'F', 0x45 for 'E' and etc.
When you put it straight in digital, like here it will remain the values as is.
unsigned char array2[10] = {0xFE,0xED,0xBA,0xBE,0xDE,0xAD,0xBE,0xEF,0xCA,0xFE};
You can transfer actually any data as binary, it is a question of representation sometimes in particular place.