Shop OBEX P1 Docs P2 Docs Learn Events
MCP4725 dac — Parallax Forums

MCP4725 dac

Trying to get the quickstart to talk to an Adafruit mcp4725 dac.
a little of JonnyMac Basic i2c.
what did i forget here...? I haven't worked with i2c before.

Comments

  • Tracy AllenTracy Allen Posts: 6,664
    edited 2017-06-06 04:46
    Not sure what version of Jon's i2c driver you are using, but one thing to check is whether it expects the 7-bit or 8-bit address. Your program above is using $62, which is the 7-bit incarnation. The 8-bit re-incarnation is shifted 1 bit left, and what should actually get sent to the MCP4725 either way is $C4 for write and $C5 for read.

    Another thing I notice about your program is that it writes 3 bytes. First is $62, followed by the value $FF,F0 you want to appear for the 12 bit output, left justified in 2 bytes. I haven't used that chip, but the way I read the data sheet, there are two modes for writing the output. The normal mode expects 4 bytes, S,$C4, $40, $FF, $F0,P, where the 3rd and 4th bytes are the desired output, left justified, S=start, P=stop. The second mode, which they call fast mode, expects 3 bytes, S,$C4,$0F,$FF,P, where the 12 bit data is in the 2nd byte low nibble and 3rd byte. The upper nibble of the second byte determines the mode. The data sheet says that fast mode can accept additional byte pairs separated by stops, without need for repeated start or ID.
  • Thanks Tracy,


    I looked over JonnyMacs code for the mpc4728, and some Arduino code, and some pic code... and the
    data sheet... and have only gotten more confused.
    thank you for your support.

    I'll give all that a try, and report back...
  • Hot Dog!... that did it! Thanks.
    Now that I can talk to the 4725, I will have it generate a saw tooth to drive a yig oscillator.
    I have the prop talking to a processing program, so now I will have a sweep gen interfaced to my pc.
    Fun stuff....

    Bill
  • JonnyMacJonnyMac Posts: 9,104
    edited 2017-06-07 06:29
    Since my name was bandied about in this thread <grin>, I thought I'd knock together a quick object.

    IT IS NOT TESTED (I don't have an MCP4725). That said, it does compile without error. The attached archive includes my latest basic I2C object. Note that it requires a pull-up on SCL and SDA.

    In the start() and startx() methods, the address is the lower three bits of the 7-bit device address OR'd with the state of your A0 pin.

    If you'd be so kind as to give this a try and report back, I would be grateful.

  • OK Jon,
    I ran my simple test program with your jm_i2c and it is working fine...
    note, however I'm not doing any fancy stuff, like writing to the mcp4725 eeprom. maybe later..
    also i am just doing the top 8 bits to generate the ramp. (takes about 350 ms)
    as this project progresses i plan to do all 12 bits, and I would like it to do it a little faster...
    Thank you for your support.

    Bill
  • JonnyMacJonnyMac Posts: 9,104
    edited 2017-06-07 18:13
    Remember that the idea of an object is to encapsulate working code so that grunt interface details don't interfere with the flow of your high-level program. You can treat an object as a black box, though I enjoy that the Propeller makes it very easy to open said box.

    You indicated that the device code for the units you have is $62. To use my [WIP] object -- assuming you're sharing the Propeller EE pins -- you would do this:
    dac.start(%010)
    
    This will connect to the I2C bus and set the internal ID for other processes. You can verify that the device is ready like this:
    check := dac.present
    
    If check is true, you're ready to roll. You can promote an 8-bit value to 12 bits by shifting it left.
    dac.write_dac(value << 4, %10)
    

  • Tracy AllenTracy Allen Posts: 6,664
    edited 2017-06-07 19:57
    Fast mode might look something like this to generate a ramp or waveform segment. Data sheet figure 6.1.
    PUB write_wave(wordArrayPointer, length)   | idx
       ' all array words are %0000xxxx_xxxxxxxx  data right justified
      i2c.start
      i2c.write(devid)
      repeat idx from 0 to length-1
        i2c.write(word[wordArrayPointer][idx] >> 8)   ' top 4 bits will be %0000 for fast mode
        i2c.write(word[wordArrayPointer][idx])      ' the least significant 8 bits
       '  insert pacing here
      i2c.stop
    
  • Ok Jon and Tracy,
    I'm working on it. I will try the DAC fast mode. The way I started it will take ~5 seconds
    to do 4096 points. I would like that to be more like 1 sec. The 4725 data sheet sez it can
    do 3.4 Mbit/sec... did I see a fast i2c routine in the obx ?
    I'm 74 years old, so I'm not too fast myself. (some have even said I'm sorta half fast. .... I think
    that's what they said...)
    Thanks again for your help, I'll let ya know how it goes.
  • Well, I have a PASM version of my I2C object that I can use. I created a second version with the DAC object with a _fast suffix; this uses my PASM I2C object, which means it uses a cog.

    Note that the way this I2C object is connected it cannot be shared with other devices. I am working on another version that gets loaded by the top object (directly), and then can be shared my multiple I2C devices. For the time being, this should give you the bump in speed you're after.
  • Bill, half fast huh? Nothing wrong with that, a tortoise and hare thing .

    if you provide more info about the kind of waveform you want, then maybe we can help to optimize the spin code. You have to realize that each simple instruction in spin is going to take something like 5 to 20 microseconds, say average 12, and to output 4096 data points in 1 second leaves only 244 microseconds between points, something like 20 instructions, 50 at the most. The i2c protocol takes quite a few instructions to set up and execute each transfer, so it will certainly pay to unload the details of i2c onto pasm, running quickly in parallel in a separate cog. That leaves your main cog to handle setting up or computing the waveform and its timing.

    Another nice pasm i2c driver in the OBEX is Chris Gadd's PASM driver v1.8od
    http://obex.parallax.com/object/700
    It has useful methods for sending words or blocks of words either low byte or high byte first, The fast mode for your '4725 expects high byte first. If your spin code points to a block of words for your waveform, it could stream that out very fast indeed. The trouble would be that it might be too fast. The object does have a bitrate parameter that could limit the transfer rate, or, it would be easy enough to add a pasm command to set an inter-word pacing parameter.



  • OK Tracy,

    I am a retired EE and ham radio guy. I have a number of yig tuned oscillators and yig tuned
    band pass filters... the drivers for the yigs require 0 – 10v. I am building (hobby) two things..
    a signal sweep generator and a “poor man's” spectrum analyzer. I have managed to get the
    prop to talk to a processing program on my pc.
    For the signal gen project I just want to enter the start and stop freqs on the pc.. and maybe the
    sweep speed.
    For the spectrum analyzer I would like to enter start, stop and then have the prop return a byte
    for each 'step' to indicate a signal amplitude (8 bits) . I know it will be slow, however a 12 bit
    sweep, triangle wave, would cover 2 to 14 Ghz. So most likely would be sweeping only a small
    part of that (maybe 1/10 or less)
    So it's just a hobby, but having the prop talk high speed 115k to the processing program and
    plotting a graph on the pc. Is tempting .
    I also have played with PICs and chips are cheap... so this could become distributed processing.

    I will play with the code you guys have provided. May take a few days, weeks....
    Thank you for your support.

    Bill
    weaton001@gmail.com
  • Jon, you might take a look and see what I forgot here.
  • JonnyMacJonnyMac Posts: 9,104
    edited 2017-06-09 18:31
    I know you're new, so now that I've finished swearing in the privacy of my own office, please allow me to offer this guidance: objects are not meant to be clobbered with application code -- it defeats the purpose of a reusable object.

    Hierarchically, your project would be structured thusly if using my objects.

    Project Code
    .... jm_mcp4725_fast
    .... .... jm_i2c_fast

    This implies that the child objects (....) are not touched by the user; you access them through the public methods provided.

    Don't worry, you're in good company; this is a common error. My projects and objects are always prefaced with "jm_" (so that the guilty party can be identified), and I append "_demo" to high-level demonstrations of new objects. I never provided any high-level code in this thread, just objects to be used by them. That said, I did indicate how to use the standard version of my MCP4725 object (which I cannot test as I don't have that device).

    At the moment, I'm making changes to my jm_i2c_fast object which, of course, impacts the jm_mcp4725_fast object.
  • THANKS JON,
    I should have done better.... I did get it to work. I re-wrote my code, and I had to put in a short delay. it now
    works and takes 2 seconds to do 4096 points.
    and i don't know how to post my code..
    {{          test program
    testing MCP4725 D/A June 9 2017
    
      SDA      = 29          ' dac buss
      SCL      = 28
      makes a triangle  wave... June ?
      now try with jm_mcp4725_fast version   
      and to try jm_i2c_fast  NOW go
    }}
     
    Con                           
    _Clkmode = Xtal1 + pll16x       
    _Xinfreq = 5_000_000
       
    Var
      long stack1[40]        'space for pst
       long stack2[40]        'space for xxx
      word x     
           
    Obj
      'pst:          "Parallax Serial Terminal"    
      time:         "jm_time"
      dac:          "jm_mcp4725_fast"
    Pub   main 
        dira[20..21]:=1
         time.start
         dac.start(%010)
         time.pause(1)          'PAUSE MUST BE HERE, OR NO WORK...!!!
            
     repeat                 
         
          repeat x from 0 to 4095    ' for testing ...ramp up
           dac.write_dac (x,0)
                
       repeat x from 250 to 0       'faster return to 0 ramp down  
             dac.write_dac (x,0)
    
  • the above code with dac.write_fast takes 1.6 seconds for 4096 points. and the pause has to be there at the start...
  • Congrats!
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2017-06-10 15:58
    Bill,
    That's interesting about the YIG filter and oscillator. GHz right? I read a wiki refresher about the technology (fascinating) and looked them up on ebay where there seem to be lots of types at as many price points.

    Have you considered using the Propeller's build-in counter DAC for the scanning? Plus an op-amp to filter it and step it up to the 10V control range.

    About the code, I thing the fast ramp-down should start where it left off going up...
    repeat x from 4080 to 0 step 16       'faster return to 0 ramp down  
             dac.write_dac (x,0)
    
  • JonnyMacJonnyMac Posts: 9,104
    edited 2017-06-11 00:10
    Have you considered using the Propeller's built-in counter DAC for the scanning? Plus an op-amp to filter it and step it up to the 10V control range.

    I'm not very good with analog circuits, but I made this suggestion and sketched a possible circuit for one of my tech friends at the Mouse House. He built it and it worked perfectly for his 0-10V control need.

    @Bill: Propeller App Note 1 deals with counters.
  • Again guys, thank you for your support.

    And yes Tracy, the YIGs are a fascinating bit of physics . I first learned of them in the military
    50 years ago. I have one in my shop that I am planing to use in this project, that covers
    1 to 18 Ghz. Most of the yigs have built in ovens to keep them at a constant temp. and the
    drive circuits are constant current (adjusts for the wire resistance change with temp).
    I think with the 12bit d/a they will be quite accurate. We will see...
    What I want to do is:
    have a serial 115_200 connection to the pc processing program to tell the prop sweep program
    start and stop numbers.(this starts it)
    I have an ADC0820 8 bit fast a/d connected to a port. (can sample in a few microseconds)
    I would like to have the a/d take a sample after each step and send one byte back the serial connection
    @ 115_200. So at max that would be 4096 bytes returned for one max sweep .

    So with all the objects spread out over 8 cogs... does that sound do-able? In 2 seconds?
  • This is the latest , next is to do the adc0820 a/d object.
    
    DAC_tester_working_3.spinDAC_tester_working_3.spin                        
  • 
    i still have no idea how to post the code....                          
  • Fifth symbol from the left "C" gives you the code no code boundaries for posting your dode. Hit the "C" and paste code between ] [.
    Jim
Sign In or Register to comment.