See the attached project.
What I am trying to do is read data, then send it to a 0-5V DAC chip (MCP4725). I have no problem reading the data, but when it goes from my data buffer to be written to the I2C port, something bad happens, because the DAC does not respond correctly. See the picture I captured in the zip file. The value of the data is, correctly, 0xA1, or 161. If I manually retype '0xA1' in the bracket instead of putting the variable, it works like it is supposed to. Do I need to 'cast' the hex as an integer somehow, or inform the compiler what type of data it is?
I know, this is probably extremely basic C programming stuff, but please bear with me.
You declared value as uint32, but should be uint16. Then when
you write to DAC you need to split it into 2 bytes, uint8, and write
those out to the 12 bit DAC. Mask off upper 8 get lower byte,
and mask off lower 8 and shift >> 8 bits to get upper byte.
You also do not test the I2C command return values for an error
and handle that.
The sensor takes 12 bits of data, but my input data is only 8 bit, therefore I do not need the second part (I write 0x00 to it). It does not matter what I set 'value' to (int, int32, uint8). If I use it from a variable, it will not work with the component, but if I simply pause the program, read what the value is as a hex, then type it in (0x96 in this case) then it works perfectly. See the attached image. Any idea what is happening here?
I understand the I2C is not good form, but that is not what is at issue here, as I can hard code the value and the transfer works perfectly. It is not until I use a variable that it breaks down.
Thank you for your input,
As Dana said "You also do not test the I2C command return values for an error and handle that."
Test that! This is at least during prototype phase the key to success.
I copied some 'proper' I2C code from an example and now it does not function at all. It gets stuck the second time around in this block of code:
while (0u == (I2C_DAC_I2CMasterStatus() & I2C_DAC_I2C_MSTAT_WR_CMPLT))
/* Waits until master completes write transfer */
The I2C component worked flawlessly beforehand (besides not accepting the variable data source) Sort of frustrating. Any ideas?
Thank you for your help,
I turned off the striplights because I saw they were interfering with some interrupts, and sure enough, my problem with the 'WR_CMPLT' hanging went away.
However, the I2C component still does not respond at all to the variable input into the buffer, but works just fine if I hard code the exact same number in the program.
I am still stuck with the original issue. Any ideas?
This sounds like a issue with the ground plane and device layout on the MPC485. The data sheet says that this can be an issue with performance. "You do not have the issue with a fixed data but when the unit is running at speed you do. Cjeck 8.4 and on about layout issues..
Your DSO is your best friend here. Setting up its trigger for signals
Vss - .5 >= Vsig >= Vdd + .5 and see if it captures anything on a
suspect line or Vdd or even Vss ground bounce. If you have a 4
channel scope you can monitor 4 lines at a time. Single shot cap-
Also use infinite persistence on supply lines to see how effective
your bypassing is.
Thank you for the feedback. I tried the volatile thing, and it did not work. I guess I don't understand why if the number comes from a variable versus a constant in my program it would be affected by the board layout? Please see the attached image. The number in buffer, or variable 'test' is the culprit here. It is being read in as 0xA7, and NOT CHANGING unless I change it externally through hardware. All I have to do to get the DAC to work correctly is to write 'test = 0xA7' instead of 'test = *dmxValuePtr'. I had a test program where it would cycle through the 4096 values of the DAC each program cycle, and that worked fine as well.
Any ideas? Unfortunately I do not have a scope, and cannot test the ground plane theory without a board redesign... I have seen a number of successful boards using this chip that do not have a separate analog ground plane.
Things seem to be getting even stranger...at least for me. See the attached image. The top code does not work, but the bottome code works correctly. They are both setting buffer directly to 0xCC, but one is conditional on my incoming data. That one though it seemingly sends the same data to the DAC, does not work at all, in fact it seems to be sending 0x00 because it sets the DAC back to 0V output.
Any ideas are greatly appreciated.
Thank you in advance,
You are asking for trouble not haveing correct ground planes in a high speed analog /digital board. Ground loop signals and cause all kinds of issues with DAC and ADC devices. I worked on a military project and it had a large board of ADC and one DAC for a bit signal the ground planes where seperated and only came togetter at one point. It 's called a Star ground plane. This board was very quite and worked very well. It even had some Ferite beads on the board to help filter the high speed noise,. Check the PSOC 4 design guide lines AN57821. it shows you how to layout the board Mixed Signal Circuit Board Layout Considerations.
Thank you for your input.
I understand that this board may not be laid out in the best way possible, but isn't that just a red herring? What would the ground plane have to do with functionality based on what type of variable makes up the data being sent over the bus? The I2C bus should be sending the same value data at exactly the same levels no matter where it comes from, correct?
Did you get a chance to look at the image? Doesn't it seem strange that the compiler shows the exact same data being sent, but depending on it's source it either works or does not?
I don't think this is the issue since the DAC works without any problems at all if I send a constant over the bus. It generates exactly the voltage it is supposed to with no communication problems or fluctuations.
I do not have much experience with this, obviously, can you explain it to me?
Like this ?
// Store buffers
volatile int16 DMXbuffer;
int16 *dmxReadPtr = DMXbuffer;
Your DMBXBuffer is updated by an interrupt and sent to the DAC (I2C) in the main ... some compiler optimisations can make this scenario unreliable.
Did you already consider to give your I2C component's interrupt the highest priority? The behavior of your I2C is quite abnormal and I haven't seen that before. I would suggest (besides the interrupt) to try reducing your project piece by piece to the bare I2C communication to see what causes the error.
Are you using the built in EEprom in the Mpc4725? Your code is set to an address of 0X60 but you should be using 0x40 to talk directly to the Dac. Per the Data sheet.
SS DD capacitors of the
performance and DAC output signal integrity. Careful
board layout will minimize these effects. Bench testing
has shown that a multi-layer board utilizing a lowinductance
ground plane, isolated inputs, isolated
outputs and proper decoupling are critical to achieving
the performance that the MCP4725 is capable of
providing. Particularly harsh environments may require
shielding of critical signals. Separate digital and analog ground planes are recomended.
pin and the ground pins of the V
MCP4725 should be terminated to the analog ground plane.
Inductively-coupled AC transients and digital switching
noise from other devices can affect on DAC
The MCP4725 has a two-wire I2C™ compatible serial
interface for standard (100 kHz), fast (400 kHz), or high
speed (3.4 MHz) mode.
EEPROM - buffer specifies to write directly. 0x60 is overall device address:
// Read address DMX value and write proper voltage
buffer = 0x40;
buffer = *dmxValuePtr;
buffer = 0x00;
// I2C Transfer
(void) I2C_DAC_I2CMasterWriteBuf(MCP4725_ADDRESS, buffer, PACKET_SIZE, I2C_DAC_I2C_MODE_COMPLETE_XFER);
Yes, I read that in the datasheet. I would still please ask why that has anything to do with where the data is coming from in the processor. The I2C should send the same bytes the same way no matter where it is coming from. The chip behaves perfectly well if a constant is sent of the exact same value and outputs the correct analog voltage. Why would data from a different source in the software have a different effect because of the ground plane?
While changing the interrupt order, I managed somehow to permenantly make it so that the I2C only leaves the MasterStatus check if the UART interrupt is totally disabled, not just prioritized last. It is definitely something to do with the interrupts, but I have no idea what I am violating with them. I need both the I2C and the UART interrupts.
Thank you in advance and thank you for all the feedback. The latest minimal project is included.
When a constant value is written to the device it does not matter if a few bytes are missing. When you are using a variable data the data is only sent once and if the noise is high then it is not transferred to the device and it fails. It's hard to debug this type of error with out storage scopes and other equipment to look at the data. But it is always the best to over design the board than fix a ground or noise issue later in the design.
Thank you for the explanation. Just for my edification - what do you mean that the constant data can be missing a few bytes but the variable only gets sent once? When I write the buffer to the I2C device, doesn't the same data get sent if I set the buffer value using a variable or constant (in this example buffer = 0x100 vs. buffer = *dmxValuePtr)? Is there a way to change my variable data to avoid this? Am I totally not getting what you are saying?
What I mean if the data is a fixed value and you loop the program the data is always the same value. But if the data is variable and differnent in each loop the I2C data to the DAC fails due to noise. As I said a Logic analyzer would show the errors but you said you do not have one. Have you tried to slow the project clock down and see that happens?
Ah, sorry, I was confused.
I have tested this by incrementing the value every 'main' cycle which would simulate a constantly changing value, and it works great if it is a program variable that is not related to the data stream.
The key to the problems seems to be the data stream, but I am clueless how. Once again see the image attached.
The only difference between these two is that on the top one the buffer is only filled with 0xCC if the data stream is that value. The bottom code just directly puts 0xCC in. The bottom works, and the top does not even though the debugger shows the exact same data going to the DAC each way. This is what baffles me.
Scope, logic analyzer -
Real low cost analyzer, very capable.
This is not a solution, but your pin address read function, why
not just read the port and that value is your address.....? Get
rid of all the if ( ) test code. Just a thought.
This thread goes on and the subject of the questions changes... Tom, what is the state of your rather unusual I2C issue? Can you provide us with a complete project showing that error? I am quite sure that it has to do with something external to the I2C component as interrupts, pullup resistors etc.
Replace your line with this and it should work.
I2C_DAC_I2CMasterWriteBuf(MCP4725_ADDRESS, (unsingned char *)&buffer, PACKET_SIZE, I2C_DAC_I2C_MODE_COMPLETE_XFER);
Your are passing an array to the function but it expect a pointer. What is sent to the function is not the content of your array but the array address.
See the attached latest version of the project with the suggested changes. Unfortuantely it works the exact same way (though reading the whole port for the address sure is cleaner). Directly inputting a value into 'buffer' makes the DAC do exactly what it should, and passing a value from the data retrieved over UART makes it behave as if the value is 0 - sets the DAC voltage to 0.
The crazy thing to me is the following (see the picture I uploaded earlier): if I use the UART buffer in an if statement and set buffer directly, but only if the UART value is present, it STILL does not work correctly, even though, as well in this case, the buffer seems to have all the right values when hovered over in debug mode.
I just did an experiment to show that if I turn off the UART interrupt and set the UART data buffer value manually, it works fine this way as well. What is happening to that data when the UART interrupt is running? It seems to affect a few variables down the line that get set by the UART buffer data. Can I just shut down the interrupt while my MAIN code is running and start it back up again afterward?
Thanks so much for the help, it is appreciated.
Thank you, Bob.
Unfortunately the same thing happened to me before when I was messing around with interrupt priorities - the I2C does not get past the 'MasterStatus()' check, and gets stuck in the 'CY_NORETURN'.
Not only does it do this, but if you switch the interrupt priorites BACK to what they were it does not work like it used to, but still gets stuck here. I had to go back to an old version to get it working on both I2C and UART before. For some reason the software is permenantly altered once you change the interrupt priorities so that you cannot go back unless you have an old version. The only way it works now is if I comment out the ISR_DMX_IN_StartEx.
This is possibly a separate problem.
Just another quick piece of information. If I disconnect the data line, the program runs smoothly until I reconnect it. As soon as the UART interrupt is triggered, it ends up back in CY_NORETURN limbo again. This is probably what you might expect given the last post, but I just wanted to try to get all the information out there.
What I mean is stuck here:
* We must not get here. If we do, a serious problem occurs, so go into
* an infinite loop.
Increase stack. Check pointer accesses! This is always a user fault.
I make errors, too
Please replace (near line 57)
// If data ready, read data
while(UART_DMX_ReadRxStatus() & UART_DMX_RX_STS_FIFO_NOTEMPTY)
Thank you for the input.
This seems to have taken care of the last issue with gettings stuck - however the original I2C issue remains. Still not budging the DAC unless I feed it the data from one of my variables.
So I am pretty sure I found out what my issue is.
The DMXbuffer has been overrunning and the pointer going past the buffer size. I had another application with this code, and for whatever reason, it worked fine even though it must have been overrunning there as well. There was no I2C in that one, it just passed a shifted data buffer on to another device. I am not sure why it is, I must be missing the break that signals a new data packet now and again.
So - the band-aid has been to catch an overrun and just dump the rest of the data until a new packet is recognized. It works fine, but not the best solution...
Another question is - how do I 'poll' the pointer to find out where it is in the buffer?
Thank you for all your help, even though this was an issue that was unanticipated, you helped me clean up my code in a number of places and taught me a lot about interrupts and the system itself.