Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Embarassing SPI question (re: Netduino Helper Max72197221.cs )


  • Please log in to reply
28 replies to this topic

#1 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 27 August 2013 - 10:38 PM

I have one of the typical cheapo four-module 7219 LED matrices (e.g. four panels of 8x8 each, with power, ground, clock and cs daisy-chained; each module after the first gets it's DIN from the previous DOUT).  I downloaded the current Max72197221.cs from Netduino Helpers, and was able to send letters and patterns to the first module.  So far ,so good.

 

However, according to the 7219 datasheet, I should be able to address each of the subsequent modules by sending a No-Op after each row of data (the four 7219 chips acting as a shift register) for each module I want to move to the right.  But sending what I think is the right value to the matrix doesn't work.

 

For example, I changed the original Max72197221.cs code to define SpiBuffer as ushort[2], and modified the Write subroutine as follows:

 

protected void Write(byte register, byte value) {

SpiBuffer[0] = register;

SpiBuffer[0] <<= 8;

SpiBuffer[0] |= value;

 

SpiBuffer[1] = (byte)RegisterAddressMap.NoOp;

SpiBuffer[1] <<= 8;

SpiBuffer[1] |= 0;

 

Spi.Write(SpiBuffer);

}

 

But the display doesn't - well, display.  It also doesn't work when defining SpiBuffer[1] simply as 0 (without all the bit-shifting nonsense).  Can anyone help me spot the flaw in my logic?



#2 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 28 August 2013 - 06:09 AM

Assuming (haven't read the d/s) the first matrix will swallow the nop only passing forward the next operation to the next matrix in chain, shouldn't you be sending the nop first when addressing the second matrix?



#3 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 28 August 2013 - 11:49 AM

I thought so too, and actually tried it that way first.  It didn't work either.  And researching the 7219 online lead to my trying it the other way.

 

Such a simple thing, and so frustrating!



#4 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 28 August 2013 - 04:44 PM

The built-in 16 bit shiftregister has the effect of data coming in though DIN being spit out through DOUT 16.5 clock cycles later. So there's a 16 bit delay before data reaches the 2nd chip. Data is latched in from the shiftreg on the high edge of CS. So you are correct, the nop should go last. I agree, your code ought to work, after clocking through 32 bits, the nop should be sitting in the shiftregister of the 1st matrix and the actual op in the 2nd. As LOAD goes high (!CS goes low) ending the SPI write, the shiftregister data should be latched in to both chips respectively. At this point, the 1st would load the nop while the 2nd would load the op. What happens if you send the same op twice in a row (eg without a nop in between) - does both chips act as expected or only the 1st, 2nd or none? EDIT: I've had some bad experiences with 16 bit SPI, could be you have to make two transfers, 16 + 16 bits. Naturally, if you have a scope or analyzer, have a look at the signals to what really happens and check your wiring, SPI timing config with setup time, etc.

#5 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 28 August 2013 - 05:59 PM

Same thing: when I tried sending live data twice, only the first module responded.  I'm reletively certain it's not a hardware issue, as I was able to get all leds on all modules to light using my Bus Pirate.

 

The two transfers is a good idea; I'll give that a try. 

 

Do you think the new enhanced SPI (which permits transferes other lengths than 8 or 16 bits) might help?

 

Thank you as always, my friend, for taking the time to help.



#6 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 28 August 2013 - 06:30 PM

The thing to look for is probably that LOAD doesn't go high in between bytes but only in between 16 bit words otherwise the chips might get confused. I'm not sure, but I don't think the ushort array SPI methods actually use 16 bit word length so trying explocit variable word length SPI is definately worth a try. A thing to try next where you are more in control of the signals, would be to bitbang the thing, i.e using gpio for CLK, DIN and LOAD/!CS. That way you could check to see if the chip really works the way you think it would. If it does, it's probably an SPI thing.

#7 JoopC

JoopC

    Advanced Member

  • Members
  • PipPipPip
  • 148 posts

Posted 28 August 2013 - 07:11 PM

I have modified/added  and make ik work well.

 

Here is the class thats work with a cascade display:

Imports SystemImports Microsoft.SPOTImports System.Collections''' <summary>''' I do not need to reinvent the wheel twice !''' thanks to embedded labs and Fabien Royen for there examples,  http://embedded-lab.com/blog/?p=6644 ; ''' http://fabienroyer.wordpress.com/2011/03/13/using-a-max7219max7221-led-display-driver-with-a-netduino/''' ''' based on these examples and modified to fit in our project.''' </summary>''' <remarks></remarks>'Public Class Max7219   Protected m_Spi As MultiSPI4SevenSegment   Private CharAddressMap As Hashtable   Private _digits As Byte   Private _Devices As Integer   Public Enum RegisterAddressMap As Byte      DecodeMode = &H9      Intensity = &HA      ScanLimit = &HB      Shutdown = &HC      DisplayTest = &HF   End Enum   Public Enum DisplayRegisterFormat As Byte      ShutdownMode = &H0      NormalOperation = &H1   End Enum   Public Enum Intensity As Byte      One      Two      Three      Four      Five      Six      Seven      Eight      Nine      Ten      Eleven      Twelve      Thirteen      Fourteen      Fifteen      Sixteen      Max = &H15      Min = &H0      Average = &H8   End Enum   ''' <summary>   ''' Netduino Pin:   ''' D11 - SPI MOSI --> DIn of Board   ''' D13 - SPI CLK --> CLK of Board            ''' </summary>   ''' <param name="chipSelect">Load pin of Board</param>   '''    Public Sub New(ByVal chipSelect As Cpu.Pin, ByVal Devices As Integer, Optional ByVal Digits As Byte = 8)      _digits = Digits      _Devices = Devices      Spi = New MultiSPI4SevenSegment(New SPI.Configuration(chipSelect, False, 0, 0, False, True, 1000, Hardware.SPI.SPI_module.SPI1))      Initialize()   End Sub   Public Sub Initialize()      CharAddressMap = New Hashtable()      CharAddressMap.Add("0", &H7E)      CharAddressMap.Add("1", &H30)      CharAddressMap.Add("2", &H6D)      CharAddressMap.Add("3", &H79)      CharAddressMap.Add("4", &H33)      CharAddressMap.Add("5", &H5B)      CharAddressMap.Add("6", &H5F)      CharAddressMap.Add("7", &H70)      CharAddressMap.Add("8", &H7F)      CharAddressMap.Add("9", &H7B)      CharAddressMap.Add("A", &H77)      CharAddressMap.Add("B", &H1F)      CharAddressMap.Add("C", &H4E)      CharAddressMap.Add("D", &H3D)      CharAddressMap.Add("E", &H4F)      CharAddressMap.Add("F", &H47)      CharAddressMap.Add("G", &H5E)      CharAddressMap.Add("H", &H37)      CharAddressMap.Add("I", &H30)      CharAddressMap.Add("J", &H3C)      CharAddressMap.Add("K", &H2F)      CharAddressMap.Add("L", &HE)      CharAddressMap.Add("M", &H55)      CharAddressMap.Add("N", &H15)      CharAddressMap.Add("O", &H1D)      CharAddressMap.Add("P", &H67)      CharAddressMap.Add("Q", &H73)      CharAddressMap.Add("R", &H5)      CharAddressMap.Add("S", &H5B)      CharAddressMap.Add("T", &HF)      CharAddressMap.Add("U", &H3E)      CharAddressMap.Add("V", &H1C)      CharAddressMap.Add("W", &H5C)      CharAddressMap.Add("X", &H49)      CharAddressMap.Add("Y", &H3B)      CharAddressMap.Add("Z", &H6D)      CharAddressMap.Add(" ", &H0)      CharAddressMap.Add(".", &H80)      CharAddressMap.Add("-", &H1)      CharAddressMap.Add("_", &H8)      EnableDigits(_digits)      DisplayTest(False)      ShutDown(False)      Clear(_digits)   End Sub      Public Sub WriteTxt(value As String, decPointOnDigit As Integer, ByVal startDigit As Byte, Optional ByVal cascadeDevice As Integer = 0)      Const cnstDecPoint As Byte = 128      Const cnstLeftToRight As Byte = 1      If value.Length > 8 Then         value = value.Substring(0, 8)      End If      Dim texts As Char() = value.ToCharArray()      Dim displayText As String      For i As Integer = 0 To texts.Length - 1         displayText = texts(i).ToString().ToUpper()         If CharAddressMap.Contains(displayText) Then            If i = decPointOnDigit Then               Write(startDigit, Convert.ToByte(CharAddressMap(displayText).ToString()) + cnstDecPoint, cascadeDevice)            Else               Write(startDigit, Convert.ToByte(CharAddressMap(displayText).ToString()), cascadeDevice)            End If         End If         startDigit -= cnstLeftToRight      Next   End Sub   Public Sub Write(register As Byte, value As Byte, Optional ByVal cascadeDevice As Integer = 0)      SyncLock Spi         Dim WriteRegister(cascadeDevice) As UShort         WriteRegister(0) = CUShort(CUShort(register) * 2 ^ 8) Or value         For t As Integer = 1 To cascadeDevice Step 1            WriteRegister(t) = &H0         Next         Spi.Write(WriteRegister)         Thread.Sleep(2)         Spi.Write(New UShort() {&H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0})      End SyncLock   End Sub   Public Sub Clear(Optional ByVal numberOfDigits As Byte = 8)      For device As Integer = 0 To _Devices - 1         For i As Byte = 1 To numberOfDigits            Write(i, &H0, device)            Thread.Sleep(2)         Next      Next   End Sub   Public Sub SetIntensity(ByVal intensity As Intensity)      For device As Integer = 0 To _Devices - 1         Write(CByte(RegisterAddressMap.Intensity), CByte(intensity), device)      Next   End Sub   Public Sub DisplayTest(ByVal enable As Boolean)      For device As Integer = 0 To _Devices - 1         If enable Then            Write(CByte(RegisterAddressMap.DisplayTest), &H1, device)         Else            Write(CByte(RegisterAddressMap.DisplayTest), &H0, device)         End If      Next   End Sub   Public Sub ShutDown(ByVal shutDown__1 As Boolean)      For device As Integer = 0 To _Devices - 1         If shutDown__1 Then            Write(CByte(RegisterAddressMap.Shutdown), &H0, device)         Else            Write(CByte(RegisterAddressMap.Shutdown), &H1, device)         End If      Next   End Sub   Private Sub EnableDigits(ByVal numberOfDigits As Byte)      For device As Integer = 0 To _Devices - 1         Write(CByte(RegisterAddressMap.ScanLimit), CByte(numberOfDigits - 1), device)      Next   End Sub   Public Property Spi() As MultiSPI4SevenSegment      Get         Return m_Spi      End Get      Set(value As MultiSPI4SevenSegment)         m_Spi = value      End Set   End Property   Public Sub dispose()      Try         If Spi IsNot Nothing Then            Spi.Dispose()         End If      Catch ex As Exception      End Try      Spi = Nothing      Thread.Sleep(100)   End SubEnd Class


#8 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 28 August 2013 - 07:29 PM

Very nice!  I'll try this as soon as I get home from work tonight!  I'll keep you posted.

 

I note the Thread.Sleep in your write method - perhaps my problem was a timing issue?

 

Thanks to both of you!



#9 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 28 August 2013 - 10:18 PM

I should probably ask if you could share the code for MultiSPI4SevenSegment. 



#10 JoopC

JoopC

    Advanced Member

  • Members
  • PipPipPip
  • 148 posts

Posted 29 August 2013 - 06:00 AM

I should probably ask if you could share the code for MultiSPI4SevenSegment. 

The MultiSpi is based on the class from Stephan Thoolen with the exception that you can dispose the class.

Because the SolarLogging program is such a large project we cut as much code we can to fit in memory.

If Visual Basic is to simple for you, you can convert the snippet to your favorite java clone C# with http://www.developer...t/csharp-to-vb/

''' <summary>''' extended class to manage multiple seven segment displays''' </summary>''' <remarks></remarks>Public Class MultiSPI4SevenSegment   Implements IDisposable   Protected _SPIDevice As SPI   Protected _Configuration As SPI.Configuration   Public ReadOnly Property Config() As SPI.Configuration      Get         Return Me._Configuration      End Get   End Property   Public Sub Dispose() Implements IDisposable.Dispose      If Me._SPIDevice IsNot Nothing Then         Try            Me._SPIDevice.Dispose()         Catch ex As Exception         End Try         Me._SPIDevice = Nothing      End If   End Sub   Public Sub New(ByVal config As SPI.Configuration)      Me._Configuration = config      If Me._SPIDevice Is Nothing Then         Try            Me._SPIDevice = New SPI(Me._Configuration)         Catch ex As Exception         End Try      End If   End Sub   Public Sub Write(ByVal WriteBuffer As Byte())      Me._SPIDevice.Config = Me._Configuration      Me._SPIDevice.Write(WriteBuffer)   End Sub   Public Sub Write(ByVal WriteBuffer As UShort())      Me._SPIDevice.Config = Me._Configuration      Me._SPIDevice.Write(WriteBuffer)   End SubEnd Class


#11 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 29 August 2013 - 12:06 PM

I like simple!  And VB isn't simple at all - just different. 

 

I appreciate the additional information.  I'll give it a try tonight.

 

Thanks again!



#12 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 30 August 2013 - 12:32 PM

I'm an idiot.  But you all knew that already. :wub:

 

First, I couldn't get JoopC's code to work any better than mine.  So I immediately decided I had a hardware issue (despite my success with the Bus Pirate) and decided to do some research.

 

I found a post online that said thant when sending multiple bytes to the chained matrices, all bytes have to be sent within a single ChipSelect(false)/ChipSelect(true) pair.  So I went back to my original code, set the cs pin to GPIO_PIN_NONE in the SPI constructor, set up an OutputPort for the chip select (per http://forums.netdui...l=gpio_pin_none - thanks CW2!), and send my bunch of bytes. 

 

And it worked! 

 

Sort of - the intensity isn't right, and I'm getting display on every other matrix (rather than adjacent ones).  But it's a start.

 

If and when I ever get this fully working, I'll post my code.



#13 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 30 August 2013 - 01:52 PM

Good you found it and that's exactly what I meant when I wrote this:

 

The thing to look for is probably that LOAD doesn't go high in between bytes but only in between 16 bit words otherwise the chips might get confused. I'm not sure, but I don't think the ushort array SPI methods actually use 16 bit word length...



#14 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 30 August 2013 - 01:54 PM

If I'd have read that more closely, it would have saved me hours of screwing around.  I should know to pay more attention to what you suggest!

 

Thanks again!



#15 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 30 August 2013 - 02:12 PM

;)

I think variable SPI word length also would have solved the problem - did you try it using 16 bit words?



#16 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 30 August 2013 - 03:32 PM

Not yet; I'm using a Plus 2, and I believe there's a problem with SPI and variable word length.  But I won't swear to it.



#17 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 02 September 2013 - 07:37 PM

Getting there...

 

http://youtu.be/xYyo6ZzX_J0



#18 hanzibal

hanzibal

    Advanced Member

  • Members
  • PipPipPip
  • 1287 posts
  • LocationSweden

Posted 03 September 2013 - 04:55 PM

Nice, is that using variable word length or with "manual" LOAD control?

#19 remotewizard

remotewizard

    Advanced Member

  • Members
  • PipPipPip
  • 96 posts

Posted 03 September 2013 - 06:13 PM

Manual control; I ser SPI CS to PIN_NONE, and manually toggled an output port at the start and beginning of my multi-digit write.

 

My biggest problem was all the fonts I could find online were rotated 90 degrees on my cheap set of LED matrices.  I found a freeware app that let me create my own fonts, which came with a sample that worked quite nicely as-is!

 

Right now, I have the font arrays stored in a hashtable, with the equivalent character of the alphabet as the key.  Clunky, but it works. still, I'm going to just set up a simple array and use the ASCII value as the index, and see if that affects performance or memory usage.

 

Thanks again everyone for your help!



#20 Emilio x64

Emilio x64

    Advanced Member

  • Members
  • PipPipPip
  • 40 posts

Posted 04 September 2013 - 11:31 PM

I am also using a 7219 but since I don't have hardware yet (waiting for the official 4.3) I have worked with emulation by using the Extensible Emulator. Now I read this so to save me some suffering could you clarify this for me to make sure I got it right?

 

  • I understood from the 7219 that the NOOP was used to shift to the slave 7219 but since it was not explicitely mentioned I was sending the NoOP as the first 2 bytes: (a) NOOP(empty_data) and then (B) COMMAND(value). However, reading this thread it seems I had it wrong, so you say IF I want to write to the slave 7219 I would send the COMMAND(value) I need and then follow it by NOOP(dummy)  (two 16 byte transactions but with the NOOP in the 2nd transaction), right?
  • Commands are always 16 bits, address plus data, right?
  • Emulation is not the same as real hardware, according to what I read here, letting the MF SPI s/w do the Loading won't work in this setup right? so you recommend doing the SS (Chip Select/Load) manually making it active before the 2 bytes are sent and inactive AFTER the two bytes are sent.
  • A write to the slave 7219 would require only one LOAD activation/deactivation for the 32 bytes (Command[value] + NoOp[dummy]) OR do it for every 16-bit (2 byte) transaction?

 

Another problem I just noticed in my setup is that each 7219 drove a 4+2 digit setup (better than 6 individual digits) but unfortunately the 4-digit 7Segment uses 35mA and the 2-digit 7Segment uses 25mA which I guess wouldn't work well with a single Iset resistor (one set would look dimmer than the other). So I will have to probably drive the two 4-digit 7Segments by the main (same current limit) and the two 2-digit 7Segments by the slave 7219 (same current) with the complication that writing a value on either set would require two transactions (one for main to fill the first 4 digits and one for slave to fill the remaining decimal digits) :*(






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.