This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Multiple 2605L control with multiplexor

Other Parts Discussed in Thread: DRV2605L, DRV2605LEVM-MD, TCA9548A, TCA9554A, DRV2605

Hi.

I'm currently trying to control up to 8 haptic actuators using 8 drv2605L connected to a i2c mux (PCA9547). The thing is, I've read here that I should be using an i2c switch instead a mux. Why is that? I thought I could leave a driver playing while muxing to another, set the waveform, play it, mux to another and so on. Is there any technical reason for the driver to be actually unable to play an effect while i2c is disconnected?

Thank you very much,

David

  • Hi, David:

    due to the fixed I2C slave address, multiple dRV2605L cannot be connected to one I2C master.

    but I think we can use an I2C switch or mux to seperately control each one of them.

    I think we can leave the DRV2605L working while mux the I2C bus to another DRV2605L.

    I have used an I2C switch to control multiple DRV2605L (such as DRV2605LEVM-MD). when we set control path to one DRV2605L only, the I2C switch works as a I2C mux.

  • Hi Peter,

    That's what I was expecting, but when the waveform is written to its register and the GO signal is send, this waveform is played only once and the GO bit cleared. As the user guide states:

    "The GO bit remains high until the playback of the haptic waveform sequence is complete.".

    That makes me think motor only plays the waveform once. So, how can I keep the waveform playing continuously until other action is commanded?

    Thanks,
    David
  • Hi, David:

    for DRV2605L, there isn't repeat functionality implemented.
    the I2C master needs to interact with DRV2605L periodically to keep it playing.

    we have a new device coming, which has implemented the repeat functionality for waveform sequencer playback.
    it will be released in this December.
  • Hi, Peter:

    So I am considering switching to your DRV2605LEVM-MD solution. I've seen this thread where a GUI-based software is available to control the module, but I've not seen any example code so far. Is there any available example code to do this? I mean, I would like to check the module connection, as well as to be able to select what effects and what actuators are active.

    Thanks,

    David

     

  • Hi, David:

    please check the below code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;

    namespace HapticsFamily
    {
    public class DRV2605LEVM_MD : HapticsDevice
    {
    private const byte TCA9548A_ADDR = 0x70;
    private const byte TCA9554A_ADDR = 0x20;
    private const byte TCA9554A_COMM_BYTE_OUTPUT = 0x01;
    private const byte TCA9554A_COMM_BYTE_CONFIG = 0x03;

    private const byte CHANNEL_NUMBER = 8;
    private List<Channel> mChannels;

    public DRV2605LEVM_MD()
    : base(DeviceType.DRV2605LEVM_MD, "DRV2605LEVMMD")
    {
    Library = new TS2200Lib(DeviceType.DRV2605L);
    mChannels = new List<Channel>();
    for (int i = 0; i < CHANNEL_NUMBER; i++)
    {
    mChannels.Add(new Channel((byte)i, this));
    }
    }

    public override bool Connected
    {
    get
    {
    return base.Connected;
    }
    set
    {
    base.Connected = value;
    foreach (Channel ch in mChannels)
    {
    ch.DRV2605L.Connected = value;
    }
    }
    }

    public override bool ForceComm
    {
    get
    {
    return base.ForceComm;
    }
    set
    {
    base.ForceComm = value;
    foreach (Channel ch in mChannels)
    {
    ch.DRV2605L.ForceComm = value;
    }
    }
    }

    public void InitBoardData()
    {
    RegRawWrite(TCA9554A_ADDR, TCA9554A_COMM_BYTE_CONFIG, 0);

    EnableChannel(0xff);

    foreach (Channel ch in mChannels)
    {
    SetOutputChannel(ch.Mask);
    ch.InitChannel();
    }

    EnableChannel(0);
    }

    byte mDRV2605L_regReadSingle(DeviceType devType, byte page, byte addr)
    {
    return RegRead(0, addr);
    }

    int mDRV2605L_regWriteSingle(DeviceType devType, byte page, byte addr, byte value)
    {
    return RegWrite(0, addr, value);
    }

    int mDRV2605L_regSetbitSingle(DeviceType devType, byte page, byte addr, byte mask, byte value)
    {
    return RegSetbit(0, addr, mask, value);
    }

    int mDRV2605L_regWriteMultiple(DeviceType devType, byte page, byte addr, byte[] values)
    {
    return RegWrite(0, addr, values);
    }

    byte[] mDRV2605L_regReadMultiple(DeviceType devType, byte page, byte addr, ushort count)
    {
    return RegRead(0, addr, count);
    }

    int mDRV2605L_regPoll(DeviceType devType, byte page, byte addr, byte mask, byte value, ushort timeout)
    {
    return RegPoll(0, addr, mask, value, timeout);
    }

    public class Channel
    {
    public const string CHANNEL_NAME = "Driver";
    private byte mNo = 0;
    private DRV2605 mDRV2605L;
    private bool mEnabled;

    public Channel(byte number, DRV2605LEVM_MD EVM)
    {
    mNo = number;
    mEnabled = false;
    mDRV2605L = new DRV2605(DeviceType.DRV2605L);
    mDRV2605L.regReadSingle += EVM.mDRV2605L_regReadSingle;
    mDRV2605L.regWriteSingle += EVM.mDRV2605L_regWriteSingle;
    mDRV2605L.regSetbitSingle += EVM.mDRV2605L_regSetbitSingle;
    mDRV2605L.regWriteMultiple += EVM.mDRV2605L_regWriteMultiple;
    mDRV2605L.regReadMultiple += EVM.mDRV2605L_regReadMultiple;
    mDRV2605L.regPoll += EVM.mDRV2605L_regPoll;
    }

    public DRV2605 DRV2605L
    {
    get
    {
    return mDRV2605L;
    }
    }

    public EffectLibrary.EffectSequence EffectSequencer
    {
    get
    {
    return mDRV2605L.Library.EffectSequencer;
    }
    }

    public bool Enabled
    {
    get
    {
    return mEnabled;
    }
    set
    {
    mEnabled = value;
    }
    }

    public byte Mask
    {
    get
    {
    return (byte)(1 << mNo);
    }
    }

    public String Name
    {
    get
    {
    return CHANNEL_NAME + (mNo+1);
    }
    }

    public byte Index
    {
    get
    {
    return mNo;
    }
    }

    public byte[] Sequencer
    {
    get
    {
    byte[] seq = new byte[mDRV2605L.Library.EffectSequencer.Items.Count];
    for (int i = 0; i < seq.Length; i++)
    {
    seq[i] = mDRV2605L.Library.EffectSequencer.Items[i].Index;
    }

    return seq;
    }
    }

    public void InitChannel()
    {
    for(int i=0; i < mDRV2605L.Registers.Count; i++){
    Register reg = mDRV2605L.Registers.getRegisterByIndex(i);
    reg.Value = mDRV2605L.RegRead(0, reg.Address);
    }

    updateEffectSequence();
    }

    public void updateEffectSequence()
    {
    mDRV2605L.SyncLibrary();
    }

    public void FlushEffectSequence()
    {
    mDRV2605L.RegWrite(0, 0x01, 0x00);
    mDRV2605L.RegWrite(0, 0x04, Sequencer);
    }

    public byte checkGoStatus()
    {
    return mDRV2605L.RegRead(0, 0x0c);
    }

    public void Reset()
    {
    mDRV2605L.RegWrite(0, 0x01, 0x00);

    for(int i=0; i < mDRV2605L.Registers.Count; i++){
    Register reg = mDRV2605L.Registers.getRegisterByIndex(i);
    mDRV2605L.RegWrite(0, reg.Address, reg.DefaultValue);
    }
    }
    }

    public String[] getChannelNames()
    {
    List<String> names = new List<string>();

    foreach(Channel ch in mChannels){
    names.Add(ch.Name);
    }

    return names.ToArray();
    }

    public int getChannelIndex(String name)
    {
    int ret = -1;

    foreach (Channel ch in mChannels)
    {
    if (ch.Name.Equals(name))
    {
    ret = ch.Index;
    break;
    }
    }

    return ret;
    }

    public String[] getChannelEffectNames(byte ch)
    {
    List<String> names = new List<string>();

    if(ch < mChannels.Count){
    Channel channel = mChannels[ch];
    channel.DRV2605L.SyncLibrary();
    for (int i = 0; i < channel.EffectSequencer.Items.Count; i++)
    {
    names.Add(channel.EffectSequencer.Items[i].Name);
    }
    }

    return names.ToArray();
    }

    private void EnableChannel(byte mask)
    {
    RegRawWrite(TCA9554A_ADDR, TCA9554A_COMM_BYTE_OUTPUT, mask);
    }

    private void SetOutputChannel(byte mask)
    {
    RegRawWrite(TCA9548A_ADDR, mask);
    }

    public void ChangeMode(WorkMode.ModeType mode)
    {
    switch (mode)
    {
    case WorkMode.ModeType.Int_Trig:
    RegWrite(0, 0x01, 0x00);
    UpdateChxRegMap(0x01, 0x00);
    break;
    case WorkMode.ModeType.RealTime_Playback:
    RegWrite(0, 0x01, 0x05);
    UpdateChxRegMap(0x01, 0x05);
    break;
    }
    }

    private void UpdateChxRegMap(byte addr, byte val)
    {
    foreach (Channel ch in mChannels)
    {
    if (ch.Enabled)
    {
    ch.DRV2605L.Registers.getRegisterByAddr(0, addr).Value = val;
    }
    }
    }

    private void UpdateChxRegMap(byte addr, byte[] val)
    {
    foreach (Channel ch in mChannels)
    {
    if (ch.Enabled)
    {
    for (int i = 0; i < val.Length; i++)
    {
    ch.DRV2605L.Registers.getRegisterByAddr(0, (byte)(addr+i)).Value = val[i];
    }
    }
    }
    }

    public byte NumberOfEnabledChx
    {
    get
    {
    byte num = 0;

    foreach (Channel ch in mChannels)
    {
    if (ch.Enabled) num++;
    }

    return num;
    }
    }

    public byte MaskOfEnabledChx
    {
    get
    {
    byte mask = 0;

    foreach (Channel ch in mChannels)
    {
    if(ch.Enabled)
    mask |= ch.Mask;
    }
    return mask;
    }
    }

    public void SetRTPStrength(byte val)
    {
    RegWrite(0, 2, val);
    UpdateChxRegMap(0x02, val);
    }

    public void FlushChxEffectSequence()
    {
    foreach (Channel ch in mChannels)
    {
    if (ch.Enabled)
    {
    SetOutputChannel(ch.Mask);
    ch.FlushEffectSequence();
    }
    }

    SetOutputChannel(MaskOfEnabledChx);
    }

    public Boolean CheckGoStatus()
    {
    bool go = false;

    foreach (Channel ch in mChannels)
    {
    if (ch.Enabled)
    {
    SetOutputChannel(ch.Mask);
    byte val = ch.checkGoStatus();
    if (val == 0x01)
    {
    go = true;
    break;
    }
    }
    }

    SetOutputChannel(MaskOfEnabledChx);

    return go;
    }

    public void StartEffectPlayback()
    {
    ChangeMode(WorkMode.ModeType.Int_Trig);
    RegWrite(0, 0x0c, 0x01);
    UpdateChxRegMap(0x0c, 0x01);
    }

    public void StopEffectPlayback()
    {
    RegWrite(0, 0x0c, 0x00);
    UpdateChxRegMap(0x0c, 0x00);
    }

    public void ClearChxWaveformSeq()
    {
    foreach (Channel ch in mChannels)
    {
    ch.EffectSequencer.Items.Clear();
    }
    }

    public bool isChxEffectFull(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return (mChannels[ch].EffectSequencer.Items.Count < 8) ? false : true;
    }

    return true;
    }

    public bool isChxEnabled(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].Enabled;
    }

    return false;
    }

    public bool AddChannelEffect(byte ch, String effect)
    {
    bool suc = false;
    if (ch < mChannels.Count)
    {
    byte idx = Library.getEffectIndexByName(effect);
    if (idx != 0)
    {
    mChannels[ch].EffectSequencer.Items.Add(new EffectLibrary.EffectSequence.Effect(effect, idx, EffectLibrary.EffectSequence.Repeat.No_Loop));
    suc = true;
    }
    }

    return suc;
    }

    public bool AddChannelWait(byte ch, byte duration)
    {
    bool suc = false;
    if (ch < mChannels.Count)
    {
    byte idx = (byte)(duration | 0x80);
    mChannels[ch].EffectSequencer.Items.Add(new EffectLibrary.EffectSequence.Effect(null, idx, EffectLibrary.EffectSequence.Repeat.No_Loop));
    suc = true;
    }

    return suc;
    }

    public bool RemoveChannelEffect(byte ch, byte idx)
    {
    bool suc = false;
    if (ch < mChannels.Count)
    {
    if (idx < mChannels[ch].EffectSequencer.Items.Count)
    {
    mChannels[ch].EffectSequencer.Items.RemoveAt(idx);
    suc = true;
    }
    }

    return suc;
    }

    public bool SetChannelEnabled(byte ch, bool enable)
    {
    bool suc = false;

    if (ch < mChannels.Count)
    {
    mChannels[ch].Enabled = enable;
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    return suc;
    }

    public void SetChannelEnabled()
    {
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }

    public bool SwitchChannel(byte ch)
    {
    bool suc = false;

    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    }
    return suc;
    }

    public ActuatorType getChxActuatorType(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.Actuator;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void setChxActuatorType(byte ch, ActuatorType actuator)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    ChangeMode(WorkMode.ModeType.Int_Trig);
    mChannels[ch].DRV2605L.RegSetbit(0, 0x1a, 0x80, (byte)((actuator == ActuatorType.LRA) ? 0x80 : 0x00));
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public DRV26xxLoop.LoopType getChxLoopType(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.Loop;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void setChxLoopType(byte ch, DRV26xxLoop.LoopType loop)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    mChannels[ch].DRV2605L.RegWrite(0, 0x01, 0x00);
    if (mChannels[ch].DRV2605L.Actuator == ActuatorType.ERM)
    {
    mChannels[ch].DRV2605L.RegSetbit(0, 0x1d, 0x20, (byte)((loop == DRV26xxLoop.LoopType.Close_Loop) ? 0x00 : 0x20));
    }
    else
    {
    mChannels[ch].DRV2605L.RegSetbit(0, 0x1d, 0x01, (byte)((loop == DRV26xxLoop.LoopType.Close_Loop) ? 0x00 : 0x01));
    }

    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public byte getChxRegValue(byte ch, byte reg)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    byte value = mChannels[ch].DRV2605L.RegRead(0, reg);
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    return value;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public byte findChxRegValueByIndex(byte ch, int idx)
    {
    if (ch < mChannels.Count)
    {
    if(idx < mChannels[ch].DRV2605L.Registers.Count)
    return mChannels[ch].DRV2605L.Registers.getRegisterByIndex(idx).Value;
    }

    throw new IndexOutOfRangeException();
    }

    public byte findChxRegValueByAddr(byte ch, byte addr)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.Registers.getRegisterByAddr(0, addr).Value;
    }

    throw new IndexOutOfRangeException();
    }

    public int setChxRegValue(byte ch, byte reg, byte value)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    int ret = mChannels[ch].DRV2605L.RegWrite(0, reg, value);
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    return ret;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public int flushChxRegVal(byte ch, byte reg, byte value)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.RegWrite(0, reg, value);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public int getChxRegIndex(byte ch, byte reg)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.Registers.getRegisterIndexByAddr(0, reg);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void ResetChxDefault(byte ch)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);
    mChannels[ch].Reset();
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void DoChxCalibration(byte ch)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);

    mChannels[ch].DRV2605L.Calibration();

    int loop = 10;
    bool finish = false;

    while(loop > 0){
    finish = mChannels[ch].DRV2605L.checkGoState(WorkMode.ModeType.Calibration);
    if (finish)
    break;
    else
    Thread.Sleep(100);
    }
    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public bool getChxCalibrationResult(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.CalResult;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public byte getChxRegCount(byte ch)
    {
    if (ch < mChannels.Count)
    {
    return (byte)mChannels[ch].DRV2605L.Registers.Count;
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public Register getChxRegister(byte ch, int index)
    {
    if (ch < mChannels.Count)
    {
    return mChannels[ch].DRV2605L.Registers.getRegisterByIndex(index);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void refreshChxRegister(byte ch)
    {
    if (ch < mChannels.Count)
    {
    EnableChannel(mChannels[ch].Mask);
    SetOutputChannel(mChannels[ch].Mask);

    mChannels[ch].DRV2605L.RefreshRegistersByPage(0);

    EnableChannel(MaskOfEnabledChx);
    SetOutputChannel(MaskOfEnabledChx);
    }
    else
    {
    throw new IndexOutOfRangeException();
    }
    }

    public void updateChxEffectSequence()
    {
    foreach (Channel ch in mChannels)
    {
    ch.updateEffectSequence();
    }
    }
    }
    }
  • Hi Peter,

    I want to connect (and play) multiple DRV2605L to one I2C master. Therefore I want to configurate all DRV2605L with the same settings an so play the defined waveform at all actuators (ERM) connected to each DRV2605L at the same time.

    That is the same like the Mode 2 "Drivers 1 through 4 are enabled, RTP mode is setup, and all drivers are played simultaneously" of the multi driver board "DRV2605LEVM-MD".

    My questions are:

    1) Is it possible to play a RTP waveform by useing one single I2C master (like Mode2), if the multiple DRV2605L are configurated (all the same), like discribed in Mode2 (without a switch, etc.)?

    2) Is the broadcast mode useful in this case,i f the broadcast mode (performed by the DRV2605) is available at the DRV2605L I have ordered?

    Thanks and regards,

    Julian Ganser

  • Hi, Julian:

    1) i don't think so.

    2) no. as the dRV2605 has the fixed I2C slave address.

  • Hi Peter,

    could you than explane me the Mode 2 "Drivers 1 through 4 are enabled, RTP mode is setup, and all drivers are played simultaneously" of the multi driver board "DRV2605LEVM-MD".

    Isn't it what I described before (in my application)?

    Regards,

    Julian 

  • Hi, Julian:

    on DRV2605LEVM-MD, there is an I2C switch device which is a bridge to the I2C master of MCU.

    the I2C switch can be configurated to select one channel or multiple channels.

    if we use the I2C switch to select multiple DRV2605L, we can trigger the playback simultaneously.

  • Hi Peter,

    thank you for your response.

    So if the DRV2605LEVM-MD can be trigger the playpack simultanously by selecting multiple DRV2605L, it should be possible to trigger multiple DRV2605L to one I2C master in my prototyp. Am I right? 

    Would the broadcast mode useful in this case,i f the broadcast mode (performed by the DRV2605) is available at the DRV2605L I have ordered?

    Thanks a lot,

    Julian Ganser

  • Hi, Julian:

    if you use an I2C switch as what DRV2605LEVM-MD does, you can trigger multiple DRV2605L on one I2C master.
    i don't think the broadcast mode can work in this case.
  • Hi Peter,

    I've checked the code but i still have some questions. I want to make a very basic program similar to you Haptic Control (Windows only, it seems) but for Linux. Can I use this class you have posted in it? I see it derives from HapticsDevice class. Where can I find it? Do I need some TI libraries?

    Thanks.

  • Haptics_Control_Console-HEAD-2726a0c.tar.gz

    the code posted above is the whole source for Haptic Control Console.

  • Hi Peter,
    Any chance that you could shed more light on "Broadcast mode". As to what I am reading, "the only way to activate multiple actuators at the same time is using the I2C switch in broadcast mode, therefore all DRV's will get the command at the same time" is that a fair statement?
  • Hi, Bahram:

    i think broadcast mode here means "enable all channels" in the I2C switch.