Wednesday, March 28, 2012

Netduino - Prototype testing

This is just a quick update...

I am currently going through testing each part of my main control board to make sure it all works together. Hopefully all is well, but to make perfectly sure, despite some of my testing before I finished soldering everything up, I am going through every component again.

During the build I did a lot of continuity testing to make sure there were no broken tracks or joins that shouldn't exist. Powering the entire board didn't result in anything dramatic like smoke or fire, so I'm taking that as a good thing.

The main elements of the board to test in the initial phase are:

RTC
Shift Registers
TLCs

This covers the main functions and until all are working with code there’s no point moving on to the rest. I have most recently been trying out the TLC5940s for the first time. On first test with Nevyn's sample code nothing happened, on further investigation I remember that I delegated some of the logic for BLANK and VPRG to the multiplexed GPIOs, provided by the 74HC595s. I did a little bit of code reworking to incorporate Stefan's MultiSPI library and his shift register libraries and tried again. Unfortunately the code still failed and I think it is to do with the way I am trying to call the logic pins provided by the 595's. I will update further on this once I work out what is going wrong.

So far that’s 2 fails out of the 3, however thanks to some help from Stefan and no thanks to some confusion whilst soldering, I got the RTC back up and running.

Here is some sample code based on Fabien’s RTC library:


using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using netduino.helpers.Hardware;

namespace RTC
{
public class Program
{
public static void Main()
{
var clock = new DS1307();
DateTime CurrTime = DateTime.Now;
// Set the clock to some arbitrary date / time
clock.Set(CurrTime);

// Make sure the clock is running
clock.Halt(false);
while(true)
{
// Test reading RTC clock registers
Debug.Print(clock.Get().ToString());
Thread.Sleep(1000);
}

}
}
}




using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace netduino.helpers.Hardware {
///


/// This class implements a complete driver for the Dallas Semiconductors / Maxim DS1307 I2C real-time clock: http://pdfserv.maxim-ic.com/en/ds/DS1307.pdf
///

public class DS1307 : IDisposable
{
[Flags]
// Defines the frequency of the signal on the SQW interrupt pin on the clock when enabled
public enum SQWFreq { SQW_1Hz, SQW_4kHz, SQW_8kHz, SQW_32kHz, SQW_OFF };

[Flags]
// Defines the logic level on the SQW pin when the frequency is disabled
public enum SQWDisabledOutputControl { Zero, One };

// Real time clock I2C address
public const int DS1307_I2C_ADDRESS = 0x68;
// I2C bus frequency for the clock
public const int DS1307_I2C_CLOCK_RATE_KHZ = 100;

// Allow 10ms timeouts on all I2C transactions
public const int DS1307_I2C_TRANSACTION_TIMEOUT_MS = 10;

// Start / End addresses of the date/time registers
public const byte DS1307_RTC_START_ADDRESS = 0x00;
public const byte DS1307_RTC_END_ADDRESS = 0x06;

// Square wave frequency generator register address
public const byte DS1307_SQUARE_WAVE_CTRL_REGISTER_ADDRESS = 0x07;

// Start / End addresses of the user RAM registers
public const byte DS1307_RAM_START_ADDRESS = 0x08;
public const byte DS1307_RAM_END_ADDRESS = 0x3f;

// Total size of the user RAM block
public const byte DS1307_RAM_SIZE = 56;

// Instance of the I2C clock
I2CDevice clock;

public DS1307() {
clock = new I2CDevice(new I2CDevice.Configuration(DS1307_I2C_ADDRESS, DS1307_I2C_CLOCK_RATE_KHZ));
}

///


/// Gets the date / time in 24 hour format.
///

/// A DateTime object
public DateTime Get() {
byte[] clockData = new byte [7];

// Read time registers (7 bytes from DS1307_RTC_START_ADDRESS)
var transaction = new I2CDevice.I2CTransaction[] {
I2CDevice.CreateWriteTransaction(new byte[] {DS1307_RTC_START_ADDRESS}),
I2CDevice.CreateReadTransaction(clockData)
};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C transaction failed");
}

Debug.Print(clockData[4].ToString() + "/" + clockData[5].ToString() + "/" + clockData[4].ToString());

return new DateTime(
BcdToDec(clockData[6]) + 2000, // year
BcdToDec(clockData[5]), // month
BcdToDec(clockData[4]), // day
BcdToDec(clockData[2] & 0x3f), // hours over 24 hours
BcdToDec(clockData[1]), // minutes
BcdToDec(clockData[0] & 0x7f) // seconds
);
}

///


/// Sets the time on the clock using the datetime object. Milliseconds are not used.
///

///
A DateTime object used to set the clockpublic void Set(DateTime dt) {
var transaction = new I2CDevice.I2CWriteTransaction[] {
I2CDevice.CreateWriteTransaction(new byte[] {
DS1307_RTC_START_ADDRESS,
DecToBcd(dt.Second),
DecToBcd(dt.Minute),
DecToBcd(dt.Hour),
DecToBcd((int)dt.DayOfWeek),
DecToBcd(dt.Day),
DecToBcd(dt.Month),
DecToBcd(dt.Year - 2000)} )
};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C write transaction failed");
}
}

///


/// Enables / Disables the square wave generation function of the clock.
/// Requires a pull-up resistor on the clock's SQW pin.
///

///
Desired frequency or disabled///
Logical level of output pin when the frequency is disabled (zero by default)public void SetSquareWave(SQWFreq Freq, SQWDisabledOutputControl OutCtrl = SQWDisabledOutputControl.Zero)
{
byte SqwCtrlReg = (byte) OutCtrl;

SqwCtrlReg <<= 3; // bit 7 defines the square wave output level when disabled // bit 6 & 5 are unused if (Freq != SQWFreq.SQW_OFF) { SqwCtrlReg |= 1; } SqwCtrlReg <<= 4; // bit 4 defines if the oscillator generating the square wave frequency is on or off. // bit 3 & 2 are unused SqwCtrlReg |= (byte) Freq; // bit 1 & 0 define the frequency of the square wave WriteRegister(DS1307_SQUARE_WAVE_CTRL_REGISTER_ADDRESS, SqwCtrlReg); } ///


/// Halts / Resumes the time-keeping function on the clock.
/// Calling this function preserves the value of the seconds register.
///

///
True: halt, False: resumepublic void Halt(bool halt) {
var seconds = this[DS1307_RTC_START_ADDRESS];

if (halt) {
seconds |= 0x80; // Set bit 7
}
else {
seconds &= 0x7f; // Reset bit 7
}

WriteRegister(DS1307_RTC_START_ADDRESS, seconds);
}

///


/// Writes to the clock's user RAM registers as a block
///

///
A byte buffer of size DS1307_RAM_SIZEpublic void SetRAM(byte[] buffer) {
if (buffer.Length != DS1307_RAM_SIZE) {
throw new ArgumentOutOfRangeException("Invalid buffer length");
}

// Allocate a new buffer large enough to include the RAM start address byte and the payload
var TrxBuffer = new byte[sizeof(byte) /*Address byte*/ + DS1307_RAM_SIZE];

// Set the RAM start address
TrxBuffer[0] = DS1307_RAM_START_ADDRESS;

// Copy the user buffer after the address
buffer.CopyTo(TrxBuffer, 1);

// Write to the clock's RAM
var transaction = new I2CDevice.I2CWriteTransaction[] {I2CDevice.CreateWriteTransaction(TrxBuffer)};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C write transaction failed");
}
}

///


/// Reads the clock's user RAM registers as a block.
///

/// A byte array of size DS1307_RAM_SIZE containing the user RAM data
public byte[] GetRAM() {
var ram = new byte[DS1307_RAM_SIZE];

var transaction = new I2CDevice.I2CTransaction[] {
I2CDevice.CreateWriteTransaction(new byte[] {DS1307_RAM_START_ADDRESS}),
I2CDevice.CreateReadTransaction(ram)
};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C transaction failed");
}

return ram;
}

///


/// Reads an arbitrary RTC or RAM register
///

///
Register address between 0x00 and 0x3f/// The value of the byte read at the address
public byte this[byte address] {
get {
if (address > DS1307_RAM_END_ADDRESS) {
throw new ArgumentOutOfRangeException("Invalid register address");
}

var value = new byte[1];

// Read the RAM register @ the address
var transaction = new I2CDevice.I2CTransaction[] {
I2CDevice.CreateWriteTransaction(new byte[] {address}),
I2CDevice.CreateReadTransaction(value)
};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C transaction failed");
}

return value[0];
}
}

///


/// Writes an arbitrary RTC or RAM register
///

///
Register address between 0x00 and 0x3f///
The value of the byte to write at that addresspublic void WriteRegister(byte address, byte val) {
if (address > DS1307_RAM_END_ADDRESS) {
throw new ArgumentOutOfRangeException("Invalid register address");
}

var transaction = new I2CDevice.I2CWriteTransaction[] {
I2CDevice.CreateWriteTransaction(new byte[] {address, (byte) val})
};

if (clock.Execute(transaction, DS1307_I2C_TRANSACTION_TIMEOUT_MS) == 0) {
throw new Exception("I2C write transaction failed");
}
}

///


/// Takes a Binary-Coded-Decimal value and returns it as an integer value
///

///
BCD encoded value/// An integer value
protected int BcdToDec(int val) {
return ((val / 16 * 10) + (val % 16));
}

///


/// Takes a Decimal value and converts it into a Binary-Coded-Decimal value
///

///
Value to be converted/// A BCD-encoded value
protected byte DecToBcd(int val) {
return (byte)((val / 10 * 16) + (val % 10));
}

///


/// Releases clock resources
///

public void Dispose() {
clock.Dispose();
}
}
}


The next group of things to test after I get the shift registers and TLCs working will be:

SD
Wi-Fi
Serial Mux
LCD
PH
1-Wire (Waiting on 4.2 release with 1-wire support)
Auto temp probe registering 1-wire network.

Thursday, March 22, 2012

Netduino - High Power LED drivers

I am currently working on a high power LED driver; this will be used to drive my aquarium lighting.

So a brief overall spec:

The aquarium lighting rig will allow for individual control of the LEDs, this will allow for lighting effects such as cloud cover, seasons, lunar/solar cycles and lightening.

These effects along with the overall lux, colour and contrast will be achieved by having control over the LEDs individually. There will be 3 watt white Cree's, Royal Blues, Red and Greens. This combination will allow for both day and moon light and the reds and greens will allow me to adjust the colour warmth.

In total there will be 30 Leds:

14 x 3W White Cree
10 x 3W Royal Blue
3 x 3W Red
3 x 3W Green

The LEDs will be mounted on a 3mm aluminium sheet, evenly spaced, with various lenses to create an even colour and lux distribution.

The LED drivers will need to be extremely efficient i.e. above 90%, the design Magpie is proposing should achieve over 95% efficiency when used with 6 LEDs in a chain. As I will be using only 1 LED per chain this will be slightly less efficient but I am hopeful that I will still achieve at least 90%.

I will give some more specific details about the drivers and the overall build as I have more information. Currently I have the LEDs and some inductors for building the drivers, so the next steps will be to get the rest of the parts I need and in the mean time I will be testing the TLC5940s which will be providing the PWM control for the LED drivers.