Flash News: versionning

Major events are pending behind the screen of Arduinoos. A major consequence is the backlog in the answers to your code request and we are very sorry for that. A minor consequence of the pending works behind arduinoos is the new namming convention for libraries and application packages that we are happy to share.

All arduinoos libraries packages WERE nammed using the following pattern:

PlainXXX_YYYYMMDD

where:

  • XXX is a specifying suffix (e.g. DSP)
  • YYYY is the year
  • MM is the month (01 to 12)
  • DD is the day of release

All arduinoos libraries packages ARE NOW named using the following pattern:

PlainXXX_NumberLetter

where:

  • XXX is a specifying suffix (e.g. DSP)
  • Number is a sequentially attributed number starting at 1 (Major version)
  • Letter is a sequentially attributed letter starting at a (Minor version)

e.g. plainDSP_1a

All minor versions belonging to the same major version shall be compatible. In other words, minor versions will contain bug corrections (if any…) and small improvements. On the other hand, major versions will contain new functions, changes in structures. Major versions may not be backward compatible.

e.g. PlainDSP_1a functions WILL BE COMPATIBLE with Plain_DSP_1z while PlainDSP_2a functions MAY NOT BE  COMPATIBLE with Plain_DSP_1z

Congratulations Cesar, you are the first recipient of the newly named packages (actually PlainPWM_1a).

g3802

This is what you got today: a zip file featuring the new naming convention (circled in red). This zip file contains a directory (circled in blue) which it self contains code, examples and documentation (circled in green).

HTH

 

 

 

 

Memory (Part 6)

Part 123456

Previous posts on the same subject showed that it is possible to significantly increase the amount of memory for a moderate price and little hardware and software works. This post is now about the use we can do of this extra memory. The idea here is to record and then display large amounts of text on the screen of a basic 2×16 LCD unit.

letters

The use of LCD units has already been discussed here. The proposed application consists in displaying a list of pre-recorded messages. This is a two steps process: within the first phase, messages are recorded in the  EEPROM chip. In the second phase, an other application picks up the messages from the external EEPROM chip according to its need. In this way, not the least byte of Arduino’ memory is involved in message storage.

One thing to be aware of before coding is the charset of the LCD screen. Usual characters (e, 4, !, …) are encoded the same way as they are in the ASCII table but some other characters are not.

Warning : LCD units differ one from the other by means of their font maps (so as to say their tables of characters). While almost all LCD units share the ASCII codes from 32 (space) to 125 (right closing brace) , the content of other map locations may differ from unit to the other. The following tables are extracted from the datasheet of two different units from the same maker and they illustrate the changes in the font maps.  :

comparator fonts

Note: The 16 first records from the font maps are available for recording custom characters. PlainLCD contains an example of how to create and record such custom  characters.

Let’s learn how to combine the benefits from this extra memory and the use of a LCD unit with a funy application.

Have you ever heard about “Fortune” ? It runs on almost all UNIX-like (e.g. Linux, BSD, …) platforms. The purpose of Fortune is to display a random quote on request. The application is made of two separate sketches. The first sketch records quotes in the external EEPROM and the second skecth randomly picks up a quote and displays it on LCD screen.

The first sketch acts as per follow :

  • Initialize the communication with the EEPROM chip
  • Initialize the LCD display
  • Store quotes in the EEPROM chip. These quotes are split into 16 characters lines to fit the LCD screen.
  • Print the number of stored lines to give it to the second sketch

The second sketch runs the following  :

  • Initialize the communication with the EEPROM chip
  • Initialize the LCD display
  • Read 2 lines from the EEPROM chip into a buffer
  • Print this buffer onto the LCD screen
  • Go to the next line : the LCD unit will scroll vertically

No more explanations, time for coding !

Here is the sketch that stores quotes :

/*

	Example of use of PlainLCD, Liquid Crystal Display driver library
	Copyright (C) 2014 HL2 group 

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Include libraries */
#include <PlainSPI.h>
#include <PlainSPI_EEPROM.h>

/* Create objects */
PlainSPI_EEPROM MEM;

uint16_t _lines = 0; /* the number of lines stored in the EEPROM */

void setup() 
{

	Serial.begin(115200);
	InitEEPROM();
}

void loop() 
{
	FillEEPROM();
	/* print the number of lines to set it in the other application */
	Serial.print(_lines);
	Serial.println();
	while(true){};
}

void InitEEPROM()
{
	/* Reset all CS pins from PORTB */
	DDRB |= 0x07; /* Set PB0:2 as output ports */
	PORTB |= 0x07; /* Set PB0:2 to high */
	MEM.InitializeSPI_EEPROM(&PORTB, PINB2, SPI_EEPROM_25LC320);
}

void FillEEPROM()
{
	WriteSentence("Lee Segall", "A man with one watch knows what time it is; a man with two watches is never quite sure. ");
	WriteSentence("Socrates", "The unexamined life is not worth living.");
	WriteSentence("Descartes", "I think therefore I am");
	WriteSentence("Nietzsche", "Without music, life would be a mistake.");
	WriteSentence("Plato", "Wise men speak because they have something to say; fools because they have to say something.");
	WriteSentence("Chinese Proverb", "when a wise man points at the moon, the fool only looks at the finger");
}

void WriteSentence(const char* author, const char* sentence)
{
	SplitAndWriteSentence(sentence);
	SplitAndWriteSentence(author);
	SplitAndWriteSentence("****************");
}

/* split a sentence in group of 16 characters and don't split words */
void SplitAndWriteSentence(const char* sentence)
{
	uint8_t lenght = strlen(sentence);
	uint16_t cursor = 0; /* count the characters */
	uint16_t charPerLine;
	uint16_t ignoredSpace = 0;
	while(cursor < lenght){
		if(sentence[cursor] == ' '){
			cursor += 1;
			lenght += 1;
			ignoredSpace += 1;
		}
		if(cursor + 16 <= lenght){
			charPerLine = 16;
			if(sentence[cursor + 16] != ' '){ /* if a word would be cut */
				uint16_t cursorCutter = cursor+16; /* count the characters */
				while((cursorCutter > cursor) && (sentence[cursorCutter] != ' ')){ /* find the start of the word */
					cursorCutter -= 1;
				}
				if(cursorCutter != cursor){ /* if the word is not too big, set the new number of char per line*/
					charPerLine = cursorCutter - cursor + 1;
				}
			}
			WriteLineToEEPROM(sentence + cursor, charPerLine); 
			cursor += charPerLine;
		} else {
			WriteLineToEEPROM(sentence + cursor, lenght - cursor - ignoredSpace);
			break; /* the last line */
		}
	}
}

void WriteLineToEEPROM(const char* line, uint8_t size)
{
	/* write the significant text to the EEPROM */
	for(uint8_t i = 0; i < size; i++){
		MEM.WriteUInt8(line[i], (_lines<<4)+i); ;
	}
	/* fill the end of the line with spaces */
	for(uint8_t i = size; i < 16; i++){
		MEM.WriteUInt8(' ', (_lines<<4)+i); ;
	}
	_lines++;
}

And here is the sketch which prints the EEPROM content  on request:

/*

	Example of use of PlainLCD, Liquid Crystal Display driver library
	Copyright (C) 2014 HL2 group 

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

/* Include libraries */
#include <PlainSPI.h>
#include <PlainSPI_EEPROM.h>
#include <PlainLCD.h> /* Load LCD library */

/* Create objects */
PlainLCD LCD; /* HD74800-compatible, 4-bit mode*/
PlainSPI_EEPROM MEM;

/* the number of lines stored in the EEPROM retrieved from the previous sketch */
uint16_t _lines = 38; 

void setup() 
{

	InitEEPROM();
	InitLCD();
}

void loop() 
{
	for(uint16_t i = 0; i < _lines-1; i++){
		for(uint8_t j = 0; j < 16; j++){
			LCD.InsertString(MEM.ReadUInt8(j + (i<<4)), j+1);
		}
		LCD.PrintBuffer(1); /* first line */
		for(uint8_t j = 0; j < 16; j++){
			LCD.InsertString(MEM.ReadUInt8(j + ((i+1)<<4)), j+1);
		}
		LCD.PrintBuffer(2); /* second line */
		delay(1500);
	}
}

void InitLCD()
{
	/* Initialize LCD: pins must belong to the same port 
	in the order data4, data5, data6, data7, data port, reg_select, chip_enable, port 
	connect the R/W pin to ground. About the screen size, if nothing is specified,
	a 2*16 characters display is assumed */
	LCD.InitializeLCD(2, 3, 4, 5, 6, 7, &PORTD);
	LCD.ClearDisplay();
}

void InitEEPROM()
{
	/* Reset all CS pins from PORTB */
	DDRB |= 0x07; /* Set PB0:2 as output ports */
	PORTB |= 0x07; /* Set PB0:2 to high */
	MEM.InitializeSPI_EEPROM(&PORTB, PINB2, SPI_EEPROM_25LC320);
}

So far, we recorded only 38 lines. Depending on the EEPROM chip that you use you could store from 64 up to 8192 lines of 16 characters :

EEPROM_LCD_lines

Well… but there is in fact a limitation ! Before the lines are recorded in the EEPROM chip, their content must be set in the code which is responsible for writing the data in the EEPROM. The size of the data is limited by the size of the flash memory from the Arduino microprocessor (32kB for Arduino UNO). We will address this limitation in a later post.

When the compiler matters (Part 1)

Recently, I worked on a library to manage LED strips. It worked perfectly using the Window’s version of Arduino. It worked as well on the GNU/Linux’ version with a LED strip… but not with another strip ! An advanced issue for an advanced headache…

In the Arduino environment , when the “verify” or “upload” buttons buttons are pressed, a compiler is called: e.g. avr-g++ is the default compiler used by Arduino IDE. The compiler  reads the code from source file(s) (e.g. .ino, .h and .cpp) and generates an executable binary file (.hex). The resulting executable file content may vary a lot depending upon the numerous parameters involved in compilation. This post is about the testing of some of these parameters which may have an impact on the timings.

  • The compiler version which may depend on the Operating System in use
  • The use of functions or macros to encapsulate a sequence of instructions
  • The various ways of managing digital ports from the ATMEGA micro-controller:  using Atmel instructions or using Arduino’s wrapper

To understand the issue, some explanations are given about the LED strip. Each LED of the strip has a built-in controller that receives the data for itself and for the next LEDs, it treats its data and forwards the rest. This kind of controller (a WS2812) uses a protocol that requires a certain precision for the timings. Its principle is straightforward : a color is sent by a digital pin using a 24 bits stream of data. Each bit of data consists in a modulated width pulse as per the following picture :

protocol       timing_suggested

The tolerance for these timings are specified at 150 nS.

Next picture is a snap shot taken from an oscilloscope. It represents one byte of data (out of 3) issued by an Arduino board. This board is running the same source code compiled with avr-g++ on a Linux workstation and a Windows workstation :

result_OS

 Both byte send the  01010000B binary value (0×50 in hexadecimal format). The red bars show the time difference between two identical streams of bits. This shift might be a problem… or not depending upon the LED strip. In any case, one should be aware about the potential issue there.

The second way to modify the scheduling is to use a macro instead of a function. But what is the difference between a function and a macro ?

When a function is called, the context of the previous function is pushed from the processor’s registers to the memory stack :

mem_load

‘a’ is pushed from r2 then ‘b’ is pushed from r1. Some other stuff is also done to load the context of the current function.

At the opposite, defining a macro as HELLO_WOLRD here :

#define HELLO_WORLD() { Serial.print("hello world !"); }
void Setup()
{
   Serial.begin(115200);
   HELLO_WORLD();
}

Will replace the macro by its body, the code above is equivalent to :

void Setup()
{
   Serial.begin(115200);
   Serial.print("hello world !");
}

This way, no overhead is added. Another tricky C/C++ thing is the keyword “inline” before a function. Using it will ask the compiler to replace the function by its body when it is called but the compiler is able to refuse and silently disobey after analyzing the cost/benefit ratio. The two following snippets are equivalent if the compiler is in good mood :

inline void IncrementI(uint8_t i)
{
   i++;
}

void counterTo255()
{
   uint8_t i = 0;
   while(true){
      Serial.print(i);
      IncrementI();
}
void counterTo255()
{
   uint8_t i = 0;
   while(true){
      Serial.print(i);
      i++;
}

Here is the comparison between using inline functions or macro on Windows :

result_MF 

The time shifting is not a big deal, the only difference is the delay of the second byte, which may cumulatively become a problem but works with 30 LED at least.

The third parameter that was tested is the difference between the direct ports manipulation and the use of the Arduino’s wrapper :

PORTB  |= 1 << PINB4; /* direct manipulation */
digitalWrite(12, HIGH); /* using Arduino's wrapper */

Which outputs :

result_PORT2

 

The overhead introduced by the wrapper makes it not usable in this kind of application.

The compiler can also optimize the execution time or the size of the produced binary, on Arduino the default behavior is to optimize the size but it is another story. You may also want to read : disassembling code

Picoscope (Part 1)

As technology improves, the size, performances and cost of measurement instruments changes over the time. Few months ago, we described the RIGOL 1052E which was pretty new at this time. As we were looking for a new equipment for the lab we decided to compare the various products available from the market.

Before getting into the details of the data sheets from the available products, we invite you to a quick step back in the history of oscilloscopes

Early oscilloscope were heavy big beasts, they were expensive and their performances were pretty modest.

first scope

Next generations of oscilloscopes were more compact, rugged and could be moved on some occasions. They were still analog and high class instruments were capable of high frequency measurements as well as signal storage.

storage generation

The raise of digital technology had a strong  impact on oscilloscopes which were know getting very versatile and programmable. They became  stand-alone, portable, autonomous in energy.

oscilloscopes-numeriques-4-voies

Simultaneously, makers of handled measurement instruments introduced smaller autonomous and handy equipment for service and outdoor operations.

oscilloscope portable

More recently, the cost reduction of components and the mass production of equiopemenbts allowed makers to propose low cost although powerful equipment. As personal computers are available in almost any place some makers also had the idea to develop remotely controlled oscilloscopes. This post is mainly about this new type of small, powerful and affordable oscilloscopes.

Next is a quick and dirty comparison of various oscilloscopes which share common performances:

RIGOL 1052E

 rigol

Offcial RIGOL web site

FLUKE 125

fluke

Official FLUKE web site

and PicoScope 2207a

pico

Official PICO TECH web site

RIGOL 1052EFLUKE 125PICOSCOPE 2206A
Volume (dm3)6.21.30.2
Weight (Kg)2.41.20.6
Power SupplyAC poweredBattery - AC poweredUSB powered
Bandwidth (MHz)504050
Channels222
Maths functionsYesYesYes
StorageYesYesYes
Remote controlYesYesYes **
Signal generatorNoNoYes
Price (Euros)3001800500

** Remote control is mandatory

Credits :

Next post will go into the details of the retained option…

Memory (Part 5)

Part 12345, 6

The code sample from the previous post shows how to read and write bytes on the EEPROM chips. Just fine !  But what about reading and writing other types of data such as short and long integers or floating point values ? This post contains an example of use of the PlainSPI_EEPROM library which reads and writes all standard datatypes available from the Arduino’s environment .

The principle of operation of the read write test is as follows :

  • Initialize the EEPROM
  • Write sequential data of various types on the EEPROM chip
  • Read back and print these data

Initializing the EEPROM is plain simple and requires a single line of code within the setup() function:.

void setup(void) 
{
	/* Initialize serial communication */
	Serial.begin(115200);
	Serial.print("Initialize EEPROM: ");
	Serial.println();
	/* Initialize EEPROM */
	MEM.InitializeSPI_EEPROM(CS_PORT_EEPROM, CS_PIN_EEPROM, SPI_EEPROM_25LC1024);
}

One thing that you should always keep in mind is that the data types differ in size, ranging from 8 to 32 bits -hence the names – as per the following table:

types size in bits size in bytes
uint8_t, char 8 1
uint16_t, int16_t 16 2
uint32_t, int32_t, float 32 4

Since a byte is 8 bits, the correlation between the two last columns is not a surprise!

Also, keep in mind that the datatype double is not reported because it is the same as the float datatype  in Arduino’s environment. For sake of simplicity, the written values are not contiguous but placed at fixed interval of 4 bytes.  Next picture illustrates the first bytes of the EEPROM and the way the various data types are recorded :

EEPROM

Here is the testing function :

void WriteReadMixedData(void)
{
	Serial.print("Write data ...");
	Serial.println();
	uint32_t pages = MEM.Pages();
	uint16_t pageSize = MEM.PageSize();
	uint32_t data = 0;
	MEM.ActualAddress(0);
	for (uint32_t address = 0; address < (pageSize * pages); address += 4) {
		data &= 0xFF;
		switch(data % 6) {
		case 0: MEM.WriteUInt8(data); break;
		case 1: MEM.WriteInt16(data); break;
		case 2: MEM.WriteUInt16(data); break;
		case 3: MEM.WriteInt32(data); break;
		case 4: MEM.WriteUInt32(data); break;
		case 5: MEM.WriteFlt32(float(data)); break;
		}
		data += 1;
	}	
	Serial.print("Read data ...");
	Serial.println();
	MEM.ActualAddress(0);
	data = 0;
	for (uint32_t address = 0; address < (pageSize * pages); address += 4) {
		data &= 0xFF;
		Serial.print("Page: ");
		Serial.print((address / pageSize));
		Serial.print(" address: ");
		Serial.print((address % pageSize));
		Serial.print(" read: ");
		switch(data % 6) {
		case 0: Serial.print(MEM.ReadUInt8()); break;
		case 1: Serial.print(MEM.ReadInt16()); break;
		case 2: Serial.print(MEM.ReadUInt16()); break;
		case 3: Serial.print(MEM.ReadInt32()); break;
		case 4: Serial.print(MEM.ReadUInt32()); break;
		case 5: Serial.print(MEM.ReadFlt32(), 1); break;
		}
		Serial.println();
		data += 1;
	}
}

The test function is called within the loop() function :

void loop(void) 
{
	WriteReadMixedData();
	while(1); /* Run once */
}

In the next post we will describe various ways of recording and retrieving strings of characters.

 

 

Memory (Part 4)

Part 123456

So, ready to test your new EEPROM chips ? Let’s go.

For sake of simplicity, we tested the PlainEEPROM_SPI library on all chips which are available in 8 pins PDIP (Plastic Dual In Line Packages): 25LC080A, 25LC160, 25LC320, 25LC320A, 25LC640, 25LC640A, 25LC256, 25LC512 and 25LC1024. Next picture illustrates one of these chips where the pin#1 is identified with an indent or paint dot mark. All these EEPROM chips are pin to pin compatible so that you can very easily upgrade the hardware

e1

25LCxxx memory chips are featuring a SPI port and the table below contains a description of the pinout for all these chips.

PINNAME Arduino's pin description
1CS (Chip Select)10SPI allows one master, actually Arduino, to talk to many slaves : only one EEPROM in our case.
2MISO (Master In Slave Out)12On this line; data flows from the memory to the master.
3 WP (Write Protect)+5V When WP is pulled to ground; no writing to the memory is allowed.
4GroundGND
5MOSI (Master Out Slave In)11Used to send data or commands to the EEPROM.
6CLK (Clock)13 The synchronization clock generated by Arduino for its remotely controlled devices.
7HLD (Hold) +5V It suspends the transmission from the EEPROM when pulled down, hook HLD to +5V for continuous operation.
8VCC+5V

It is now time for coding the test procedures which will come along with PlainSPI_EEPROM!

The first proposed test is plain trivial. It consists in executing the following sequence of operations :

  • Initialize the EEPROM chip
  • Fill each page from  the memory with a constant
  • Read back each page and check if their content matches the written values.

The memory chip is initialized within the setup routine. Next to the initialization, it is possible to get access to global information such as the number of pages (Pages() function), the size of pages (PageSize() function) or the actual buffer pointer location (BufferPointer()).

void setup(void) 
{
	/* Reset all CS pins from PORTB */
	DDRB |= 0x07; /* Set PB0:2 as output ports */
	PORTB |= 0x07; /* Set PB0:2 to high */
	/* Initialize serial communication */
	Serial.begin(115200);
	Serial.print("Initialize EEPROM");
	Serial.println();
	MEM.InitializeSPI_EEPROM(CS_PORT_EEPROM, CS_PIN_EEPROM, SPI_EEPROM_25LC320);
	_pages = MEM.Pages();
	_pageSize = MEM.PageSize();
	_bufferPtr = MEM.BufferPointer();
	Serial.print("Pages: ");
	Serial.print(_pages);
	Serial.println();
	Serial.print("Page size: ");
	Serial.print(_pageSize);
	Serial.println();
}

The following function actually executes the test :

void RunTest(void) 
{
	/* Write pages */
	Serial.print("Write all pages "); 		
	for (uint16_t p = 0; p < _pages; p++) {
		Serial.print(".");
		uint8_t *ptr = _bufferPtr;
		for (uint16_t i = 0; i < _pageSize; i++, ptr++) {
			*ptr = _fiftyFive;
		}
		MEM.WritePage(p);
	}	
	Serial.println();
	/* Read and check all pages */
	Serial.print("Read and check all pages "); 		
	uint16_t globalErrors = 0;
	for (uint16_t p = 0; p < _pages; p++) {
		Serial.print(".");
		MEM.ReadPage(p);
		uint16_t localErrors = 0;
		uint8_t *ptr = _bufferPtr;
		for (uint16_t i = 0; i < _pageSize; i++, ptr++) {
			if (*ptr != _fiftyFive) {
				localErrors += 1;
			} 
		}
		if (localErrors != 0) {
			globalErrors += 1;
		}
	}
	Serial.println();	
	if (globalErrors != 0) {
		Serial.print("Pages in error: "); 		
		Serial.print(globalErrors); 		
		Serial.println();
	} else {
		Serial.print("No errors"); 		
		Serial.println(); 		
	}
}

Eventually, the loop function will run the test once in order to save the number of write cycles of the chips.

Reminder: While the number of read cycles is unlimited, the number of write cycles is limited to typically 1.000.000 cycles!

Thus the loop routine…

void loop(void) 
{
	RunTest();
	while(true); /* Run once */
}

The next post will contain another example which will focus on mixed types of data.

RIGOL 1052E DSO (Part 3)

Part 123

Erratic clicks!

Life goes on and the Rigol 1052E remains a reliable companion still purring and always ready to help diagnosing circuits and running prototypes testing. Sometimes ago, I experienced some strange changes in time and amplitude scales when using the notched rotary encoders. Values where jumping erratically from one value to an other, whatever the rotational speed of the knob.

I quickly found some papers on this subject. All posts are pointing to the rotary encoders themselves (I could have been worst, like firmware problem). Some suggest to disassemble the rotary encoders, some suggest to replace them. I decided to investigate the problem by my self and the next pictures illustrate the steps to the suspect components. Firstly, you will need to open the oscilloscope enclosure, as described in a previous post. Next we will need to remove few more screws in order to get access to the dashboard.

There is no need for disconnecting any cable during this procedure!

Remove the four screws which retain the power board (3 small, 1 larger screw)

ds1052e_04

Remove the two screws and nuts which retain the power cord socket

ds1052e_03

Then remove the 3 screws retaining the corners of the front plastic cover

ds1052e_01

After pulling the power card apart, remove this other screw

ds1052e_02

And this other central screw

ds1052e_05

Take the front cover apart and get access to the dashboard

ds1052e_06

All encoders are from ALPS. From my experience, these are good reliable components and I use in some of them in quite rough conditions without any complaint to make. So, how come they get defective? As in many other cases looking alike, I simply used a contact cleaner spray, right through the little slits which are visible between the black and the green parts of the encoder bodies.

ds1052e_07

After doing this and exercising the knobs again, I felt that the clicks where more “firm”, more clicking than before the cleaning, just like if some grease from the shaft migrated to the contact area. I performed a real life testing before reassembling the parts and the problem looked like it had disappeared. Putting every thing back in place did not change the RIGOL 1052′s mind and this good ol’friend is now back to operation without this pretty boring false stepping problem.

HTH

RTC Module (Part 7)

Part 123456, 7

calendar_s

Here are some new functions added to the PlainRTC libraries which drive the DS13x7, DS1306 and DS1394 series RTCs. These function are here to help in standardizing the date and time values to be recorded and read back by various applications for graphic reconstruction for example.

Although I am getting more and more a big fan of open source, my programming roots are deep in the Microsoft Visual Studio environment with whom I started in the early 90′s with VB3! I am getting quite an ol’timer… Anyway, programming Visual Studio is a nice way to delay Alzheimer!

In most data collect and data analysis applications that I designed, I used the serialized dates and times from Visual Studio, which is the same in Excel. In this way date and time are specified in a single float (32 bits) where the integer part is the number of elapsed days since the 1st of January 1900 and the fractional part is the elapsed time since 00:00:00.

Computing the number of elapsed days is not so hard as long as one keep the leap years rule in mind: every year which can be divided by 4 is a leap year except if if this year can be divided by 100 in case it is not a leap year. But there is an other exception in the Gregorian calendar: if this year can be divided by 400, IT IS a leap year! Examples are given below

  • 2000: can be divided by 400: it is a leap year
  • 2004: can be divided by 4 and not by 100: it is a leap year
  • 2100: can be divided by 4 and also by 100 (and not by 400) : it is NOT a leap year

Here is a the leap year checking function from the PlainRTC libraries:

uint8_t PlainRTC::IsLeapYear(uint16_t year)
{ 
	uint8_t res = 0x00;
	if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) {
		res = 0x01;
	}
	return(res);
}

The problem is that the programmers of Lotus-1-2-3, an almost forgotten although very popular  spreadsheet in the mid 80′s made a big mistake and programmed 1900 as a leap year! And it is not because 1900 can be divided by 4 and ALSO by 100 (And not by 400). As a consequence, entering the 29 Feb 1900 in Lotus gave no error, and serial dates between 1/1/1900 and 29/2/1900 are wrong.

At this time Microsoft was about to launch Excel and the marketing people convinced the developers to stick to the bug in order to insure full compatibility of software! So that the bug is also duplicated in the Microsoft products, including VB. Bizarre! This bug must be taken into account while programming day counters since 1/1/1900 such as in the DateSerial function from the PlainRTC libraries:

uint32_t PlainRTC::DateSerial(uint16_t year, uint8_t month, uint8_t dayOfMonth)
/* Compute date serial number since 1 Jan 1900 at 0:0:0 (which is serial number 1) */
{
	/* Integer part */
	/* 
	The "days" number must be offset by 1 because of the Lotus-1-2-3 bug 
	which was agreed by Excel for compatibility reasons. This means that DateSerial
	are wrong for dates which are less than 60	
	*/
	uint32_t days = 1;
	/* Add days from past years */
	for (uint16_t i = 1900; i < year; i++) {
		days += 365.0;
		if (IsLeapYear(i) == 0x01) {
			days += 1.0;
		}
	}
	/* Add days from past months */
	for (uint8_t i = 1; i < month; i++) {
		days += DaysInMonth(i, year);
	}
	/* Add days elapsed in this month */
	days += dayOfMonth;
	/* Build result */
	return(days);
}

 This function features an interesting sub routine which computes the number of days within any month, taking into consideration the leap years:

uint8_t PlainRTC::DaysInMonth(uint8_t month, uint16_t year)
{ 
	uint8_t days = (30 + ((month + (month >> 3)) % 2));
	if (month == 2) {
		if (IsLeapYear(year) == 0x01) {
			days -= 1;
		} else {
			days -= 2;
		}
	}
	return(days);
}

 Then I faced a frustrating situation. Designing a DateAndTimeSerial function should be as easy as summing the results from the DateSerial and the TimeSerial functions. Well, it is not! The lack of precision of the AVR floats leads to a resulting fractional value which is different from the computed TimeSerial. This means that both values should be stored apart in order to maintain a good time precision. This is no longer an issue if data is stored on an external chip.

PlainRTC libraries are available on request as usual.

HTH

Bug of the day: eMail Issue!

mail_box

I ought to care about a rather low amount of mails in arduinoos mail box! As I was very busy, I did not complain about the steady state of mail.

Fatal mistake!

For some unexplained reasons so far, a full load of emails arrived in mail box after applying some changes in the parameters of Thunderbird! So that I am now facing a serious back log that I am trying to address as quickly as possible. Thus my apology to arduinoos visitors who did not receive an answer yet. As a compensation, all writers asking for FFT, ADC, DSP code will receive a discount coupon for the product that will be released very soon non.

Thanks for understanding

Didier

Memory (Part 3)

Part 123456

chips

“Flock of eeprom chips ”

As mentioned before, EEPROM (Electrically Erasable Programmable Read Only Memory) is one type of memory which is very suitable for storing data on some occasion which would be read back as much as we like. As we deal with rather limited micro-controllers, we will skip the families of EEPROM chips which data are addressed in parallel mode. In this mode, 8 bits arranged memories require 8 pins from the micro-controller which is too much pins consuming even though the gain in speed is noticeable. Instead, we will look after chips which data is addressed in serial mode. Mainly two types of serial communication protocols are available: SPI and I2C. Both have there advantages and draw backs. My preference goes to SPI in spite of the highest number of pin required. In absolute, SPI is faster and I use this protocol a lot in my applications. So let’s go for the 25LC series of SPI EEPROM. Coming next after the 25LC chips are the 25AA chips. They differ from their supply voltages:

  • 25LCxxx: 2.5-5.5V
  • 25AAxxx: 1.8-5.5V

Within the 25LC family, some references are getting obsolete and Microchip suggests to use the A versions instead. In most cases, the A version (e.g. 25LC640A) operate at higher speeds. Care shall be taken at the B versions which have a different meaning! B versions have something to do with the way data are arranged internally. In other words, you may read carefully the chip data sheets before trying to tweak the proposed code.

As usual I am proposing a fully featured library which will allow to run any from the 25LC080, 25LC080A, 25LC080B, 25LC160, 25LC160A, 25LC160B, 25LC320, 25LC320A, 25LC640, 25LC640A, 25LC128, 25LC256, 25LC512 and 25LC1024 chips. plainEEPROM_SPI will do all the fine tuning for you, provided you can read the name on the chip and pick the correct constant name from the list as explained in the user manual file. Each chip has its own properties in terms of: Full memory size, page size, address bytes and maximum clock frequency and PlainEEPROM_SPI will care about them. Next is a short recap of the chip properties

ReferenceMax. ClockTotal sizePagesPage sizeAddress bytes
25LC102420MHz1024Kb5122563
25LC51220MHz512Kb5121282
25LC25610MHz256Kb512642
25LC12810MHz128Kb256642
25LC640A10MHz64Kb256322
25LC6402MHz64Kb256322
25LC320A10MHz32Kb128322
25LC3202MHz32Kb128322
25LC160B10MHz16Kb64322
25LC160A10MHz16Kb128162
25LC1602MHz16Kb128162
25LC080B10MHz8Kb32322
25LC080A10MHz8Kb64162
25LC08010MHz8Kb64162

Alike the most recent version of PlainSDC, PlainEEPROM_SPI is offering a set of easy to use functions which will allow to access any data at any address within the memory without the burden of memory pages.

ReadUInt8(address)
ReadUInt16(address)
ReadInt16(address)
ReadUInt32(address)
ReadInt32(address)
ReadFlt32(address)
ReadUInt8()
ReadUInt16()
ReadInt16()
ReadUInt32()
ReadInt32()
ReadFlt32()
WriteUInt8(data, address)
WriteUInt16(data, address)
WriteInt16(data, address)
WriteUInt32(data, address)
WriteInt32(data, address)
WriteFlt32(data, address)
WriteUInt8(data)
WriteUInt16(data)
WriteInt16(data)
WriteUInt32(data)
WriteInt32(data)
WriteFlt32(data)

Write and read functions are available with and without address setting. When the address argument is not set, the write or read functions are executed at the actual memory location. The value of this internally managed variable can be read or set using the ActualAddress() function form the next list.

ActualAddress()
ActualAddress(address)

Advanced user are welcome to use page oriented functions for some specific advanced programming.

ActualPage()
ActualPage(page)
Pages()
PageSize()
BufferPointer()
ClearBuffer()
ErasePage()
ErasePage(page)
EraseAll()
ReadPage()
ReadPage( page)
WritePage()
WritePage(page)

Next post on same subject