Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter fuer schwierige und grosse Gaerten im Test

RP6v2 I2C-Portexpander: Hardware

In diesem Projekt soll eine "Exp" (RP6#Experimentierplatine, CONRAD 191537) für den RP6v2 (natürlich auch für den RP6) "gebaut" werden, auf der zuerst mit dem IC PCF8574 ein I2C-Portexpander realisiert werden soll. Mit den 8 neu gewonnenen I/O-Ports kann z.B. ein LC-Display betrieben werden. Für die Teile, die man für das ganze Projekt braucht, werden Bestell-Nummern vom großen C angegeben. Natürlich gibt es auch andere Versender, bei denen es evtl. günstiger wird.

Das Projekt wird in 2 Abschnitten ("Phasen") vorgestellt. Wer mitbauen will, kann auch schon nach der ersten Phase aussteigen, weil sie ohne die 2. Phase funktioniert.

So werden die 2 (3?) Phasen aussehen:

  • Phase 1 -> Aufbau des PCF8574 (8 I/O-Ports) mit Wannenstecker für ein LCD
  • Phase 2 -> Aufbau des PCF8591 (4 AD- und 1 DA-Wandler) mit Wannenstecker für die AD-/DA-Ports
  • (Phase 3 -> Ich weiß nicht, ob es sie gibt. Aber: Ich hätte evtl. noch eine Idee dafür!)


Was braucht man allgemein für den Aufbau einer Schaltung auf der Exp:

  • Seitenschneider, Schere, Zange
  • Lötkolben 25..30 Watt, Lötzinn
  • Plastik 70 Schutzlack (CONRAD 813621)
  • Isolierter Schaltdraht YV 0,20 mm² (CONRAD 606065)
  • Versilberter CU-Draht 0,6 mm (CONRAD 605581)

Mit dem versilberten CU-Draht stellt man auf der Unterseite (= Lötseite) der Exp Verbindungen zwischen den Bauteilen her; mit dem isolierten Schaltdraht werden Drahtbrücken auf der Oberseite (= Bestückungsseite) der Exp eingesetzt. Die Lage der Verbindungen zeige ich im Bestückungsplan jeder Phase. Man muss sich nicht an die genaue Lage der Verbindungen halten.

Wenn man die Drähte und Bauteile an anderen Positionen einlötet, kann es aber sein, dass man die nächste Phase nicht mehr so aufbauen kann, wie ich das hier zeige! Möglicherweise sind die weiteren Teile dann nur noch mit einer "wilden" Freiverdrahtung machbar!


Phase 1

In der Phase 1 wird das PHILIPS Portexpander-IC PCF8574AP oder PCF8574P mit seinen Verbindungen zu einem 14-poligen Wannenstecker als Anschluß für ein LCD aufgebaut. Alle RP6#Displays können hier direkt angeschlossen werden.

Man braucht folgende Bauteile (Bestell-Nummern CONRAD) für die 1. Phase:

Anzahl Bestell-Nr. Bauteil-Bezeichnung:
1 191537 RP6 Experimentierplatine
1 Reichelt IC PCF8574AP ODER
1 Reichelt IC PCF8574P
1 189626 IC Sockel DIL 16-polig
1 741687 Wannenstecker gerade, 2 x 7-polig
1 741119 1-reihige Stiftleiste RM 2,54 mm (36-polig)
1 742902 Vier Codierbrücken (aus Set)
1 403296 Kohleschicht-Widerstand 2,2 kOhm (1/4 W)
1 430730 Trimmpoti stehend 10 kOhm (2,5 x 5mm)
1 500812 Keramik Kondensator 100 nF

Hier erst einmal der Schaltplan:

RP6v2 Portexpander SP 1.JPG

Und dann der Bestückungsplan:

RP6v2 Portexpander 1.JPG

Ich habe die Drahtbrücken aus isoliertem Schaltdraht (es sind 17) rot eingezeichnet. Sie werden auf der Oberseite der Experimentierplatine bestückt!

Viel Erfolg beim Aufbau!

Testen, ob die Schaltung grundsätzlich funktioniert, kann man mit der Demo "RP6Base_I2CMaster_01.c" aus den RP6v2 Examples. Die Demo zeigt ein Lauflicht, wenn man 8 LEDs an die Ausgangs-Ports des PCF8574 anschließt. Man sollte dazu low-current LEDs (z.B. rot 145998, grün 145971, gelb 145980) mit Vorwiderstand jeweils 1,5 kOhm (403270) an die Ausgänge anschließen,- Kathode an den Ausgang und Anode über Vorwiderstand an VDD. Die Ausgänge des PCF8574 sind zu finden an den Pins 4 (P4), 6 (P7), 11..14 (P0..P3) des LCD-Wannensteckers und an der 2-poligen Stiftleiste neben dem IC, beschriftet mit P5 P6. Auf die Jumper JP1..3 werden 3 Codierbrücken so aufgesteckt, dass die Adresseingänge des PCF8574 (A0..A2) mit GND verbunden sind (Position zum oberen Platinenrand). JP4 wird nicht aufgesteckt (INT nicht genutzt). Wenn der PCF8574AP eingesetzt wird, kann die I2C-Adresse im Demoprogramm "RP6Base_I2CMaster_01.c" unverändert bleiben. Wird jedoch der Typ PCF8574P verwendet, muss die I2C-Adresse geändert werden:

#define PCF8574_8LEDS_ADR  0x40

Später soll natürlich auch ein LCD angesteuert werden! Geduld ...


Phase 2

In der Phase 2 wird das PHILIPS Portexpander-IC PCF8591P mit seinen Verbindungen zu einem 10-poligen Wannenstecker als Anschluß für die AD- und DA-Ports aufgebaut.

Man braucht folgende Bauteile (Bestell-Nummern CONRAD) zusätzlich für die 2. Phase:

Anzahl Bestell-Nr. Bauteil-Bezeichnung:
1 152668 IC PCF8591P
1 189626 IC Sockel DIL 16-polig
1 741648 Wannenstecker gerade, 2 x 5-polig
1 460559 Elektrolyt-Kondensator 10 uF/63 V

Hier erst einmal der Schaltplan:

RP6v2 Portexpander SP 2.JPG

Und dann der Bestückungsplan:

RP6v2 Portexpander 2.JPG

Ich habe die Drahtbrücken aus isoliertem Schaltdraht (es sind zusätzlich noch 7) rot eingezeichnet. Sie werden auf der Oberseite der Experimentierplatine bestückt!

Viel Erfolg beim Aufbau!

Testen, ob die Schaltung grundsätzlich funktioniert, kann man mit der Demo "RP6Base_I2CMaster_02.c" aus den RP6v2 Examples. Die Demo zeigt das schon aus Phase 1 bekannte Lauflicht (mit dem PCF8574) und die 4 AD-Wandler Werte des PCF8591. Die angezeigten Werte werden zufällig sein, wenn an die analogen Eingangs-Ports des PCF8591 (AIN0..3) keine Schaltung angeschlossen ist. Man kann z.B. 4 LDRs zur Helligkeitsmessung verwenden. Als Vorbild kann man die Schaltung der Helligkeits-Sensoren des RP6v2 nehmen (Schaltplan RP6v2_SENSORS.pdf): Dort sind LDRs des Typs A9060 (145475) mit Widerständen 68 kOhm (403474) in Reihe geschaltet. Der ADC-Eingang liegt dann zwischen LDR und Widerstand. Die 4 Eingangs-Ports des PCF8591 findet man an den Pins 2, 1, 3, 5 (AIN0..3) des ADC/DAC-Wannensteckers. Auf die Jumper JP6, JP7 werden 2 Codierbrücken so aufgesteckt, dass die Adresseingänge des PCF8591 (A1..A2) mit GND verbunden sind (Position JP6 zum unteren, JP7 zum rechten Platinenrand). Auf JP5 wird ebenfalls eine Codierbrücke aufgesteckt (AGND = GND).

Der PCF8591 verfügt zusätzlich zu den 4 ADCs noch über einen DAC (Digital-Analog-Wandler). Dieser Ausgang (AOUT) ist verfügbar an Pin 9 des ADC/DAC-Wannensteckers. Nutzt man diese Funktion, kann man eine Spannung von 0..5 V an AOUT messen (JP5 aufgesteckt!).


Letzte Arbeiten

Was bleibt noch?

  • Wenn die Platine komplett aufgebaut ist, kann man sie mit Plastik 70 Spray auf der Lötseite gegen Korrosion schützen
  • Kontrast des LCD mit dem Poti 10 kOhm einstellen
  • Eine Befestigung für das LCD auf der Exp konstruieren
  • Sich Anwendungen überlegen
  • Anwendungen programmieren
  • ...


Allgemeine Daten und Tabellen

Stecker

Stecker Pins Bedeutung
P5 P6 2 PCF8574: I/O-Ports P5, P6
LCD 14 PCF8574: LCD-Wannenstecker (I/O-Ports P0..P3, P4, P7)
OSC 1 PCF8591: Oszillator Ein-/Ausgang
ADC/DAC 10 PCF8591: ADC/DAC-Wannenstecker (AIN0..AIN3, AOUT)

LCD

Pin Port LCD Bedeutung
1 GND VSS
2 VDD +5 V
3 Vo Kontrast
4 P4 RS Daten/Befehle
5 RW = GND
6 P7 EN Enable
7 D0 = GND
8 D1 = GND
9 D2 = GND
10 D3 = GND
11 P0 D4 Daten LSB
12 P1 D5 Daten
13 P2 D6 Daten
14 P3 D7 Daten MSB

ADC/DAC

Pin Port Bedeutung
1 AIN1 Analog Eingang 1
2 AIN0 Analog Eingang 0
3 AIN2 Analog Eingang 2
4 AGND Analog GND
5 AIN3 Analog Eingang 3
6 AGND Analog GND
7 AGND Analog GND
8 AGND Analog GND
9 AOUT Analog Ausgang
10 VDD +5 V

Jumper

Zeichenerklärung:

  • Zweipolige Jumper:
    • Stellung ON = Jumper aufgesteckt (Kontakt geschlossen)
    • Stellung OFF = Jumper abgezogen (Kontakt offen)
  • Dreipolige Jumper:
    • Stellung O = Jumper oben * aufgesteckt
    • Stellung U = Jumper unten * aufgesteckt
    • Stellung L = Jumper links * aufgesteckt
    • Stellung R = Jumper rechts * aufgesteckt

Zu *) Platine so gesehen, wie im Bestückungsplan!

Jumper Stellung Bedeutung
JP1 O (S) PCF8574: A0 = 0
JP1 U PCF8574: A0 = 1
JP2 O (S) PCF8574: A1 = 0
JP2 U PCF8574: A1 = 1
JP3 O (S) PCF8574: A2 = 0
JP3 U PCF8574: A2 = 1
JP4 ON PCF8574: INT verwenden
JP4 OFF (S) PCF8574: INT nicht verwenden
JP5 ON (S) PCF8591: AGND = GND
JP5 OFF PCF8591: AGND variabel
JP6 O PCF8591: A1 = 1
JP6 U (S) PCF8591: A1 = 0
JP7 L PCF8591: A2 = 1
JP7 R (S) PCF8591: A2 = 0

Zu (S) Standard-Stellung der Jumper!

I2C-Adressen

A2 A1 A0 PCF8574P PCF8574AP PCF8591P
0 0 0 0x40 0x70 0x90
0 0 1 0x42 0x72 nicht nutzbar *
0 1 0 0x44 0x74 0x94
0 1 1 0x46 0x76 nicht nutzbar *
1 0 0 0x48 0x78 0x98
1 0 1 0x4A 0x7A nicht nutzbar *
1 1 0 0x4C 0x7C 0x9C
1 1 1 0x4E 0x7E nicht nutzbar *

Zu *) Da A0 = 0 festgelegt!


RP6v2 I2C-Portexpander: Software

Phase 1

I2C-LCD-Libraries

RP6v2 Base und CONTROL M32

Die RP6v2 I2C-LCD Library für die RP6v2 Base und CONTROL M32 gehört in den Ordner \RP6Lib\RP6common.

Die Library stellt für das I2C-LCD dieselben Befehle zur Verfügung, die von der RP6Control Library für das an die RP6 CONTROL M32 angeschlossene LCD bereit gestellt werden (siehe Anleitung zur RP6 CONTROL M32, S. 14, Kapitel 3.1.6. LC-Display!). Die Library kann sowohl mit der RP6v2 Base, als auch mit der RP6 CONTROL M32 (jeweils als I2C Master) eingesetzt werden.

Lediglich die Funktionsnamen ändern sich: Anstelle z.B. der Funktion initLCD() der RP6Control Library heißt dieselbe Funktion der I2C-LCD Library initI2CLCD(). In allen Funktionsnamen wird also der Teil "...LCD..." durch "...I2CLCD..." ersetzt.

Die I2C-LCD Library bietet vier zusätzliche Funktionen:

  • void writeLongI2CLCD(int32_t number, uint8_t base)
  • void writeLongLengthI2CLCD(int32_t number, uint8_t base, uint8_t length)
  • void writeDoubleI2CLCD(double number, uint8_t width, uint8_t prec)
  • void writeDoubleExpI2CLCD(double number, uint8_t prec, uint8_t flags)

Diese Funktionen ermöglichen die Ausgabe von Long (int32_t) und Double (32-Bit Gleitkomma) Variablen auf dem I2C-LCD.

Beispiele:

writeLongI2CLCD(43724, HEX);          ==> Ausgabe: aacc
writeLongI2CLCD(43724, BIN);          ==> Ausgabe: 1010101011001100
writeLongLengthI2CLCD(43724, DEC, 8); ==> Ausgabe: 00043724
writeLongLengthI2CLCD(43724, HEX, 6); ==> Ausgabe: 00aacc
writeDoubleI2CLCD(123.45678, 10, 4);  ==> Ausgabe:   123.4568
writeDoubleExpI2CLCD(123.45678, 3, DTOSTR_PLUS_SIGN); ==> Ausgabe: +1.235e+02
Header

Datei RP6I2ClcdTWI.h:

/* ****************************************************************************
 *                           _______________________
 *                           \| RP6  ROBOT SYSTEM |/
 *                            \_-_-_-_-_-_-_-_-_-_/         		 >>> COMMON
 * ----------------------------------------------------------------------------
 * ------------------------ [c]2012 - Dirk ------------------------------------
 * ****************************************************************************
 * File: RP6I2ClcdTWI.h
 * Version: 1.0
 * Target: RP6 Base & Processor Expansion - ATMEGA32 @8.00 or 16.00MHz
 * Author(s): Dirk
 * ****************************************************************************
 * Description:
 * This is the RP6I2ClcdTWI header file.
 * You have to include this file, if you want to use the library
 * RP6I2ClcdTWI.c in your own projects.
 *
 * ****************************************************************************
 * THE CHANGELOG CAN BE FOUND AT THE END OF THIS FILE!
 * ****************************************************************************
 */

#ifndef RP6I2CLCDTWI_H
#define RP6I2CLCDTWI_H

/*****************************************************************************/
// Includes:

#include "RP6I2ClcdTWIConfig.h"		// Configure the target system

// Select includes depending on RP6I2ClcdTWIConfig.h:
#ifdef RP6BASE
	#include "RP6RobotBaseLib.h"	// The RP6 Robot Base Library.
#else
#ifdef RP6CONTROL
	#include "RP6ControlLib.h"	// The RP6 Control M32 Library.
#else
	#error DEFINE "RP6BASE" OR "RP6CONTROL" AS TARGET IN RP6I2ClcdTWIConfig.h
#endif
#endif
#include "RP6I2CmasterTWI.h"		// Include the I2C-Bus Master Library

/*****************************************************************************/
// Defines:

#ifndef HEX
	#define HEX 16
#endif
#ifndef DEC 
	#define DEC 10
#endif
#ifndef OCT
	#define OCT 8
#endif
#ifndef BIN
	#define BIN 2
#endif

// Max. precision of the dtostre/dtostrf conversion:
#ifndef PRECISION
	#define PRECISION		6
#endif
// dtostre flags:
#ifndef DTOSTR_ALWAYS_SIGN
	#define DTOSTR_ALWAYS_SIGN	0x01	// ' ' for positive numbers
#endif
#ifndef DTOSTR_PLUS_SIGN 
	#define DTOSTR_PLUS_SIGN	0x02	// '+' instead of ' '
#endif
#ifndef DTOSTR_UPPERCASE
	#define DTOSTR_UPPERCASE	0x04	// 'E' instead of 'e' for the exponent
#endif

/*****************************************************************************/
// I2C-LCD functions:

// A shadow register that simplifies the I2C to LCD communication:
typedef union {
	uint8_t byte;
	struct {
		unsigned LCDD : 4;
		unsigned RS   : 1;
		unsigned P5   : 1;
		unsigned P6   : 1;
		unsigned EN   : 1;
	};
} i2clcdPort_t;
extern i2clcdPort_t i2clcdPort;

void setI2CLCDD(uint8_t lcdd);
void write4BitI2CLCDData(uint8_t data);
void writeI2CLCDCommand(uint8_t cmd);
void initI2CLCD(void);

void clearI2CLCD(void);
void clearPosI2CLCD(uint8_t line, uint8_t pos, uint8_t length);

void writeCharI2CLCD(uint8_t ch);
void writeNStringI2CLCD_P(const char *pstring);
#define writeStringI2CLCD_P(__pstr) writeNStringI2CLCD_P((PSTR(__pstr)))
void writeStringI2CLCD(char *string);
void writeStringLengthI2CLCD(char *string, uint8_t length, uint8_t offset);

void _showScreenI2CLCD_P(const char *line1, const char *line2);
#define showScreenI2CLCD(__line1,__line2); ({_showScreenI2CLCD_P((PSTR(__line1)),(PSTR(__line2)));})

void writeIntegerI2CLCD(int16_t number, uint8_t base);
void writeIntegerLengthI2CLCD(int16_t number, uint8_t base, uint8_t length);
void writeLongI2CLCD(int32_t number, uint8_t base);
void writeLongLengthI2CLCD(int32_t number, uint8_t base, uint8_t length);
void writeDoubleI2CLCD(double number, uint8_t width, uint8_t prec);
void writeDoubleExpI2CLCD(double number, uint8_t prec, uint8_t flags);

void setCursorPosI2CLCD(uint8_t line, uint8_t pos);

#endif

/******************************************************************************
 * Additional info
 * ****************************************************************************
 * Changelog:
 * 
 *  ---> changes are documented in the file "RP6I2ClcdTWI.c"
 *
 * ****************************************************************************
 */

/*****************************************************************************/
// EOF
Library

Datei RP6I2ClcdTWI.c:

/* ****************************************************************************
 *                           _______________________
 *                           \| RP6  ROBOT SYSTEM |/
 *                            \_-_-_-_-_-_-_-_-_-_/         		 >>> COMMON
 * ----------------------------------------------------------------------------
 * ------------------------ [c]2012 - Dirk ------------------------------------
 * ****************************************************************************
 * File: RP6I2ClcdTWI.c
 * Version: 1.0
 * Target: RP6 Base & Processor Expansion - ATMEGA32
 *         with LC-Display 16x2 chars (CONRAD 190911)
 *         [or Backlight Display 16x2 chars (CONRAD 191621)]
 *         connected to the RP6v2 I2C-Bus with a PHILIPS PCF8574
 *         I2C port expander.
 * Author(s): Dirk
 * ****************************************************************************
 * Description:
 * This is my RP6v2 I2C-LCD Library. It contains functions for driving an
 * LCD with an I2C port expander PCF8574 on the I2C-Bus. The PCF8574 may be
 * built up on an RP6v2 experiment board (CONRAD 191537) as shown here:
 *     ---> http://www.rn-wissen.de/index.php/RP6v2_I2C-Portexpander <---
 *
 * Connection of the LCD to the PCF8574:
 *  PCF8574   LCD
 *   P0..P3   D4..D7
 *   P4       RS
 *   P5, P6   n.c.
 *   P7       EN
 * LCD D0..D3 and LCD RW are connected to GND.
 * This pin allocation is compatible with "RN-Wissen", BASCOM-AVR: AN #118
 * (lcd_i2c.lib) and "Asuro Wiki: LCD Erweiterung".
 *
 * ****************************************************************************
 * THE CHANGELOG CAN BE FOUND AT THE END OF THIS FILE!
 * ****************************************************************************
 */
 
/*****************************************************************************/
// Includes:

#include "RP6I2ClcdTWI.h"

/*****************************************************************************/
// I2C-LCD functions:

// All I2C-LCD routines are prepared to control a 2x16 character LCD.
// If you want to connect a bigger LCD you need to change some things in 
// these routines! (especially in the initI2CLCD, setCursorI2CLCD and
// showScreenI2CLCD routines)

// A shadow register for the I2C to LCD communication:
i2clcdPort_t i2clcdPort;

char lcd_tmp_buffer[17];

/**
 * Sets new 4-bit data for the LCD and also pulses the enable line of the
 * LCD to 'inform' the LCD about the new data.
 */
void setI2CLCDD(uint8_t lcdd)
{
	i2clcdPort.LCDD = lcdd;
	i2clcdPort.EN = 1;
	I2CTWI_transmitByte(PCF8574_LCD_ADR, i2clcdPort.byte);
	delayCycles(50);
	i2clcdPort.EN = 0;
	I2CTWI_transmitByte(PCF8574_LCD_ADR, i2clcdPort.byte);
}

/**
 * Write a 8bit-byte in two nibbles of 4bit to the LCD.
 */
void write4BitI2CLCDData(uint8_t data)
{
	setI2CLCDD(data >> 4);
	setI2CLCDD(data);
	delayCycles(150);
}

/**
 * Write a command to the LCD.
 */
void writeI2CLCDCommand(uint8_t cmd)
{
	i2clcdPort.RS = 0;
	write4BitI2CLCDData(cmd);
	delayCycles(150);
}

/**
 * Initialize the LCD. Always call this before using the I2C-LCD! 
 *
 */
void initI2CLCD(void)
{
	//delayCycles(34000); No need for Power ON delay as usually the
	// Bootloader should have been executed before...
	setI2CLCDD(0b0011);
	delayCycles(18000);
	setI2CLCDD(0b0011);
	delayCycles(5500);
	setI2CLCDD(0b0011);
	delayCycles(5500);
	setI2CLCDD(0b0010);
	delayCycles(5500);
	writeI2CLCDCommand(0b00101000);
	delayCycles(5500);
	writeI2CLCDCommand(0b00001000);
	delayCycles(5500);
	writeI2CLCDCommand(0b00000001);
	delayCycles(5500);
	writeI2CLCDCommand(0b00000010);
	delayCycles(5500);
	writeI2CLCDCommand(0b00001100);
	delayCycles(5500);
}

/**
 * Clears the whole LCD!
 */
void clearI2CLCD(void)
{
	writeI2CLCDCommand(0b00000001);
	delayCycles(5500);
}

/**
 * Clears some characters after the given position.
 */
void clearPosI2CLCD(uint8_t line, uint8_t pos, uint8_t length)
{
	setCursorPosI2CLCD(line, pos);
	while(length--)
		writeCharI2CLCD(' ');
}

/**
 * Write a single character to the LCD.
 *
 * Example:
 *
 *			writeCharI2CLCD('R');
 *			writeCharI2CLCD('P');
 *			writeCharI2CLCD('6');
 *			writeCharI2CLCD(' ');
 *			writeCharI2CLCD('0');
 *			writeCharI2CLCD(48); // 48 is ASCII code for '0'
 *			writeCharI2CLCD(49); // '1'
 *			writeCharI2CLCD(50); // '2'
 *			writeCharI2CLCD(51); // '3'
 *			//...
 *
 *			would output:
 *			RP6 00123
 *			at the current cursor position!
 *			use setCursorPosI2CLCD function to move the cursor to a 
 *			different location!
 */
void writeCharI2CLCD(uint8_t ch)
{
	i2clcdPort.RS = 1;
	write4BitI2CLCDData(ch);
	delayCycles(50);
}

/**
 * Writes a null terminated string from flash program memory to the LCD.
 * You can use the macro writeStringI2CLCD_P(STRING); instead, this macro
 * ensures that the String is stored in program memory only!
 *
 * Example:
 *
 *			writeNStringI2CLCD_P(PSTR("RP6 Control"));
 *
 *			// There is also a Macro that makes life easier and
 *			// you can simply write:
 *			writeStringI2CLCD_P("RP6 Control");
 *
 */
void writeNStringI2CLCD_P(const char *pstring)
{
    uint8_t c;
    for (; (c = pgm_read_byte_near(pstring++)); writeCharI2CLCD(c));
}

/**
 * Writes a String from SRAM to the LCD.
 */
void writeStringI2CLCD(char *string)
{
	while(*string)
		writeCharI2CLCD(*string++);
}

/**
 * Writes a string with specified length and offset from SRAM to the LCD.
 * If it is a null terminated string, output will be stopped at the
 * end. It does not need to be null terminated, but it is recommended
 * to use only null terminated strings/buffers, otherwise the function could
 * output any SRAM memory data stored after the string until it reaches a 0
 * or the specified length!
 *
 * Example:
 *
 *			writeStringLengthI2CLCD("RP6 Robot Sytem",16,0);
 *			// would output: "RP6 Robot Sytem\n"
 *			writeStringLengthI2CLCD("RP6 Robot Sytem",11,4);
 *			// would output: "Robot System"
 * 			writeStringLengthI2CLCD("RP6 Robot Sytem",40,4);
 *			// would output: "Robot System"
 *			// No matter if the specified length is 40 characters!
 *
 */
void writeStringLengthI2CLCD(char *string, uint8_t length, uint8_t offset)
{
	for(string = &string[offset]; *string && length; length--)
		writeCharI2CLCD(*string++);
}

/**
 * This function is useful for displaying text screens on the LCD.
 * It clears the whole LCD and writes the two Strings to line 1 and
 * line 2.
 */
void _showScreenI2CLCD_P(const char *line1, const char *line2)
{
	clearI2CLCD();
	writeNStringI2CLCD_P(line1);
	setCursorPosI2CLCD(1, 0);
	writeNStringI2CLCD_P(line2);
}

/**
 * Write a number (with specified base) to the LCD.
 *
 * Example:
 *
 *			// Write a hexadecimal number to the LCD:
 *			writeIntegerI2CLCD(0xAACC,16);
 *			// Instead of 16 you can also write "HEX" as this is defined in the
 *			// RP6I2ClcdTWI.h :
 *			writeIntegerI2CLCD(0xAACC, HEX);
 *			// Other Formats:
 *			writeIntegerI2CLCD(1024,DEC);  	// Decimal
 *			writeIntegerI2CLCD(511,OCT);	// Octal
 *			writeIntegerI2CLCD(0b11010111,BIN); // Binary
 */
void writeIntegerI2CLCD(int16_t number, uint8_t base)
{
	itoa(number, &lcd_tmp_buffer[0], base);
	writeStringI2CLCD(&lcd_tmp_buffer[0]);
}

/**
 * Same as writeIntegerI2CLCD, but with defined length.
 * This means this routine will add leading zeros to the number if length is
 * larger than the actual value or cut the upper digits if length is smaller
 * than the actual value.
 *
 * Example:
 *
 *			// Write a hexadecimal number to the LCD:
 *			writeIntegerLengthI2CLCD(0xAACC, 16, 8);
 *			// Instead of 16 you can also write "HEX" as this is defined in the
 *			// RP6I2ClcdTWI.h :
 *			writeIntegerLengthI2CLCD(0xAACC, HEX, 8);
 *			// Other Formats:
 *			writeIntegerLengthI2CLCD(1024,DEC,6);  	// Decimal
 *			writeIntegerLengthI2CLCD(511,OCT,4);	// Octal
 *			writeIntegerLengthI2CLCD(0b11010111,BIN,8); // Binary
 */
void writeIntegerLengthI2CLCD(int16_t number, uint8_t base, uint8_t length)
{
	char buffer[17];
	itoa(number, &buffer[0], base);
	int8_t cnt = length - strlen(buffer);
	if(cnt > 0) {
		for(; cnt > 0; cnt--, writeCharI2CLCD('0'));
		writeStringI2CLCD(&buffer[0]);
	}
	else 
		writeStringLengthI2CLCD(&buffer[0], length, -cnt);
}

/**
 * Write a long number (with specified base) to the LCD.
 *
 * Example:
 *
 *			// Write a hexadecimal number to the LCD:
 *			writeLongI2CLCD(0xAACC,16);
 *			// Instead of 16 you can also write "HEX" as this is defined in the
 *			// RP6I2ClcdTWI.h :
 *			writeLongI2CLCD(0xAACC, HEX);
 *			// Other Formats:
 *			writeLongI2CLCD(1024,DEC);  	// Decimal
 *			writeLongI2CLCD(511,OCT);	// Octal
 *			writeLongI2CLCD(0b11010111,BIN); // Binary
 */
void writeLongI2CLCD(int32_t number, uint8_t base)
{char buffer[33];
	ltoa(number, &buffer[0], base);
	writeStringI2CLCD(&buffer[0]);
}

/**
 * Same as writeLongI2CLCD, but with defined length.
 * This means this routine will add leading zeros to the number if length is
 * larger than the actual value or cut the upper digits if length is smaller
 * than the actual value.
 *
 * Example:
 *
 *			// Write a hexadecimal number to the LCD:
 *			writeLongLengthI2CLCD(0xAACC, 16, 8);
 *			// Instead of 16 you can also write "HEX" as this is defined in the
 *			// RP6I2ClcdTWI.h :
 *			writeLongLengthI2CLCD(0xAACC, HEX, 8);
 *			// Other Formats:
 *			writeLongLengthI2CLCD(1024,DEC,6);	// Decimal
 *			writeLongLengthI2CLCD(511,OCT,4);	// Octal
 *			writeLongLengthI2CLCD(0b11010111,BIN,8); // Binary
 */
void writeLongLengthI2CLCD(int32_t number, uint8_t base, uint8_t length)
{char buffer[33];
	ltoa(number, &buffer[0], base);
	int8_t cnt = length - strlen(buffer);
	if(cnt > 0) {
		for(; cnt > 0; cnt--, writeCharI2CLCD('0'));
		writeStringI2CLCD(&buffer[0]);
	}
	else 
		writeStringLengthI2CLCD(&buffer[0], length, -cnt);
}

/**
 * Write a floating point number to the LCD.
 *
 * Example:
 *
 *			// Write a floating point number to the LCD (no exponent):
 *			writeDoubleI2CLCD(1234567.890, 11, 3);
 *
 * The value of prec (precision) defines the number of decimal places.
 * For 32 bit floating point variables (float, double ...) 6 is
 * the max. value for prec (7 relevant digits).
 * The value of width defines the overall number of characters in the
 * floating point number including the decimal point. The number of
 * pre-decimal positions is: (width - prec - 1).
 */
void writeDoubleI2CLCD(double number, uint8_t width, uint8_t prec)
{char buffer[width + 1];
	dtostrf(number, width, prec, &buffer[0]);
	writeStringI2CLCD(&buffer[0]);
}

/**
 * Write a floating point number to the LCD.
 *
 * Example:
 *
 *			// Write a floating point number to the LCD (with exponent):
 *			writeDoubleExpI2CLCD(-123.456, 3, 0);
 *			// ... if you want a '+' sign for positive numbers:
 *			writeDoubleExpI2CLCD(+123.456, 3, DTOSTR_PLUS_SIGN);
 *
 * The value of prec (precision) defines the number of decimal places.
 * For 32 bit floating point variables (float, double ...) 6 is
 * the max. value for prec (7 relevant digits).
 */
void writeDoubleExpI2CLCD(double number, uint8_t prec, uint8_t flags)
{char buffer[prec + 8];
	dtostre(number, &buffer[0], prec, flags);
	writeStringI2CLCD(&buffer[0]);
}

/**
 * Sets the cursor position on LCD.
 */
void setCursorPosI2CLCD(uint8_t line, uint8_t pos)
{
	pos |= 128;
	if(line == 1) pos += 0x40;
	writeI2CLCDCommand(pos);
}

/******************************************************************************
 * Additional info
 * ****************************************************************************
 * Changelog:
 * - v. 1.0 (initial release) 28.05.2012 by Dirk
 *
 * ****************************************************************************
 */

/*****************************************************************************/
// EOF

RP6 CCPRO M128

Die RP6v2 I2C-LCD Library für die RP6 CCPRO M128 gehört in den Ordner \RP6CCLib.

Die Library stellt für das I2C-LCD dieselben Befehle zur Verfügung, die von der RP6CC Library für das an die RP6 CCPRO M128 angeschlossene LCD bereit gestellt werden (siehe Anleitung zur RP6 CCPRO M128, S. 25, Kapitel 3.1.8. LC-Display!).

Lediglich die Funktionsnamen ändern sich: Anstelle z.B. der Funktion RP6_initLCD() der RP6CC Library heißt dieselbe Funktion der I2C-LCD Library RP6_initI2CLCD(). In allen Funktionsnamen wird also der Teil "...LCD..." durch "...I2CLCD..." ersetzt.

Die I2C-LCD Library bietet zwei zusätzliche Makros:

  • printWordI2CLCD(__WRD__, __BASE__, __MINW__)
  • printFloatI2CLCD(__FLT__, __DECP__)

Diese Makros ermöglichen die Ausgabe von Word und Float Variablen auf dem I2C-LCD.

Beispiele:

WordVar = 12345; printWordI2CLCD(WordVar, DEC, 0);   ==> Ausgabe: 12345
WordVar = 12345; printWordI2CLCD(WordVar, DEC, 8);   ==> Ausgabe: 00012345
WordVar = 12345; printWordI2CLCD(WordVar, HEX, 0);   ==> Ausgabe: 3039
WordVar = 12345; printWordI2CLCD(WordVar, BIN, 0);   ==> Ausgabe: 11000000111001
WordVar = 12345; printWordI2CLCD(WordVar, BIN, 16);  ==> Ausgabe: 0011000000111001
FloatVar = 123.45678; printFloatI2CLCD(FloatVar, 2); ==> Ausgabe: 123.46
FloatVar = 123.45678; printFloatI2CLCD(FloatVar, 4); ==> Ausgabe: 123.4568

Datei RP6I2ClcdCClib.cc:

/* ****************************************************************************
 *                           _______________________
 *                           \| RP6  ROBOT SYSTEM |/
 *                            \_-_-_-_-_-_-_-_-_-_/              >>> CCPRO M128
 * ----------------------------------------------------------------------------
 * ----------------------------- [c]2012 - Dirk -------------------------------
 * ****************************************************************************
 * File: RP6I2ClcdCClib.cc
 * Version: 1.0 - CompactC
 * Target: RP6 CCPRO M128 - C-Control PRO M128 @14.7456MHz
 *         mit LC-Display 16x2 Zeichen (CONRAD 190911)
 *         [oder Backlight Display 16x2 Zeichen (CONRAD 191621)]
 *         verbunden mit dem RP6v2 I2C-Bus durch einen PHILIPS PCF8574
 *         I2C Port Expander.
 * Author(s): Dirk
 * ****************************************************************************
 * Beschreibung:
 * Dies ist meine RP6 CCPRO M128 I2C-LCD Bibliothek. Sie enthält Funktionen,
 * um ein LCD mit einem I2C Port Expander PCF8574 über den I2C-Bus anzu-
 * steuern. Der PCF8574 kann auf einer RP6v2 Experimentierplatine (CONRAD
 * 191537) aufgebaut werden, wie hier gezeigt wird:
 *     ---> http://www.rn-wissen.de/index.php/RP6v2_I2C-Portexpander <---
 *
 * Verbindung des LCDs mit dem PCF8574:
 *  PCF8574   LCD
 *   P0..P3   D4..D7
 *   P4       RS
 *   P5, P6   n.c.
 *   P7       EN
 * LCD D0..D3 und LCD RW sind mit GND verbunden.
 * Diese Belegung ist kompatibel mit "RN-Wissen", BASCOM-AVR: AN #118
 * (lcd_i2c.lib) und "Asuro Wiki: LCD Erweiterung".
 *
 * ****************************************************************************
 * CHANGELOG FINDEN SIE AM ENDE DIESER DATEI!
 * ****************************************************************************
 */

 // Alles nur EINMAL einbinden!
#ifndef __RP6I2CLCDCCLIB__
#define __RP6I2CLCDCCLIB__


/******************************************************************************/
// I2C-LCD:

// Ein "Schattenregister" für die I2C - LCD Kommunikation:
byte i2clcdPort;

#define LCD_RS 16;
#define LCD_EN 128;

#define HEX 16
#define DEC 10
#define OCT 8
#define BIN 2

// ----------------------------------------------------------------------------
// Makros zur Ausgabe auf dem I2C-LCD:

char __tmp_txt_i2clcd__[32];

/**
 * Sendet einen Text an das I2C-LCD.
 */
#define printI2CLCD(__STRING__) {__tmp_txt_i2clcd__ = __STRING__;\
 writeStringI2CLCD(__tmp_txt_i2clcd__);}

/**
 * Beschreibt eine bestimmte Zeile des I2C-LCDs.
 */
#define printlnI2CLCD(__STRING__, __LINE__, __POS__) {__tmp_txt_i2clcd__ = __STRING__;\
 writeLineI2CLCD(__tmp_txt_i2clcd__, __LINE__, __POS__);}

/**
 * Konvertiert eine Zahl (Int) in ASCII Zeichen und schreibt
 * diese auf das I2C-LCD.
 */
#define printIntegerI2CLCD(__INT__) {Str_WriteInt((__INT__),__tmp_txt_i2clcd__,0);\
 writeStringI2CLCD(__tmp_txt_i2clcd__);}

/**
 * Konvertiert eine Zahl (Word) in ASCII Zeichen und schreibt
 * diese auf das I2C-LCD.
 */
#define printWordI2CLCD(__WRD__, __BASE__, __MINW__) {Str_WriteWord((__WRD__),(__BASE__),__tmp_txt_i2clcd__,0,__MINW__);\
 writeStringI2CLCD(__tmp_txt_i2clcd__);}

/**
 * Konvertiert eine Zahl (Float) in ASCII Zeichen und schreibt
 * diese auf das I2C-LCD.
 */
#define printFloatI2CLCD(__FLT__, __DECP__) {Str_WriteFloat((__FLT__),(__DECP__),__tmp_txt_i2clcd__,0);\
 writeStringI2CLCD(__tmp_txt_i2clcd__);}

/**
 * Schreibt zwei komplette Zeilen Text auf das I2C-LCD.
 */
#define showScreenI2CLCD(__STRING1__, __STRING2__) {clearI2CLCD();\
 printI2CLCD(__STRING1__); setCursorPosI2CLCD(1, 0); printI2CLCD(__STRING2__); }


/**
 * Sendet ein einzelnes Daten Byte über den I2C Bus.
 */
void RP6_writeByte(byte adr, byte data)
{
    Thread_Lock(1);
    I2C_Start();
    I2C_Write(adr);
    I2C_Write(data);
    I2C_Stop();
    Thread_Lock(0);
}

/**
 * Sendet ein 4 Bit Nibble an das I2C-LCD.
 */
void setI2CLCDD(byte lcdd)
{
    Thread_Lock(1);
    i2clcdPort = i2clcdPort & 0xf0;
    i2clcdPort = i2clcdPort | lcdd | LCD_EN;
    RP6_writeByte(PCF8574_LCD_ADR, i2clcdPort);
    delayCycles(6);
    i2clcdPort = i2clcdPort & ~LCD_EN;
    RP6_writeByte(PCF8574_LCD_ADR, i2clcdPort);
    delayCycles(6);
    Thread_Lock(0);
}

/**
 * Sendet ein Kommando an das I2C-LCD.
 */
void writeI2CLCDCommand(byte cmd)
{
    Thread_Lock(1);
    i2clcdPort = i2clcdPort & ~LCD_RS;
    setI2CLCDD(cmd >> 4);
    setI2CLCDD(cmd);
    delayCycles(1);
    Thread_Lock(0);
}

/**
 * Sendet ein Zeichen an das I2C-LCD.
 */
void writeCharI2CLCD(char ch)
{
    Thread_Lock(1);
    i2clcdPort = i2clcdPort | LCD_RS;
    setI2CLCDD(ch >> 4);
    setI2CLCDD(ch);
    delayCycles(1);
    Thread_Lock(0);
}

/**
 * Initialisiert das I2C-LCD - muss immer aufgerufen werden bevor
 * man das I2C-LCD verwenden kann!
 */
void RP6_initI2CLCD(void)
{
    Thread_Lock(1);
    delayCycles(2500);
    setI2CLCDD(0x03);
    delayCycles(2500);
    setI2CLCDD(0x03);
    delayCycles(2500);
    setI2CLCDD(0x03);
    delayCycles(2500);
    setI2CLCDD(0x02);
    delayCycles(2500);
    writeI2CLCDCommand(0x0028);
    delayCycles(2500);
    writeI2CLCDCommand(0x0008);
    delayCycles(2500);
    writeI2CLCDCommand(0x0001);
    delayCycles(2500);
    writeI2CLCDCommand(0x0002);
    delayCycles(2500);
    writeI2CLCDCommand(0x000C);
    delayCycles(2500);
    Thread_Lock(0);
}

/**
 * Sendet einen Text-String an das I2C-LCD.
 */
void writeStringI2CLCD(char text[])
{
    Thread_Lock(1);
    word i, size;
    size = Str_Len(text);
    for(i = 0; i < size; i++)
        writeCharI2CLCD(text[i]);
    Thread_Lock(0);
}

/**
 * Beschreibt eine bestimmte Zeile des I2C-LCDs mit einem Text.
 */
void writeLineI2CLCD(char text[], byte text_line, byte pos)
{
    Thread_Lock(1);
    setCursorPosI2CLCD(text_line, pos);
    word i, size;
    size = Str_Len(text);
    for(i = 0; i < size; i++)
        writeCharI2CLCD(text[i]);
    Thread_Lock(0);
}

/**
 * Setzt die Cursor Position auf dem I2C-LCD.
 */
void setCursorPosI2CLCD(byte text_line, byte pos)
{
    pos = pos | 128;
    if(text_line == 1) pos = pos + 0x40;
    writeI2CLCDCommand(pos);
}

/**
 * Löscht den gesamten Inhalt des I2C-LCDs.
 */
void clearI2CLCD(void)
{
    Thread_Lock(1);
    writeI2CLCDCommand(0x01);
    delayCycles(25);
    Thread_Lock(0);
}

/**
 * Löscht eine bestimmte Anzahl von Zeichen beginnend an einer bestimmten Stelle.
 */
void clearPosI2CLCD(byte text_line, byte pos, byte length)
{
    Thread_Lock(1);
    setCursorPosI2CLCD(text_line, pos);
    while(length--)
        writeCharI2CLCD(' ');
    Thread_Lock(0);
}

#endif

/******************************************************************************
 * Info
 * ****************************************************************************
 * Changelog:
 * - v. 1.0 (initial release) 04.06.2012 by Dirk
 *
 * ****************************************************************************
 */

/*****************************************************************************/
// EOF

I2C-LCD-Demos

Die folgenden I2C-LCD-Demoprogramme sind eine Anpassung des "Example_01_LCD" Beispiels (RP6Control_LCD.c) für die RP6 CONTROL M32. Die Demos zeigen den Text "HELLO WORLD" oder "HALLO WELT!", der sich auf dem I2C-LCD hin und her bewegt.

RP6v2 Base

Config

Die Konfigurations-Datei der RP6v2 I2C-LCD Library gehört in den jeweiligen Programmordner.

Datei RP6I2ClcdTWIConfig.h:

/**
 * Settings for the RP6I2ClcdTWI Library
 */ 

#ifndef RP6I2CLCDCONFIG_H
#define RP6I2CLCDCONFIG_H


/**
 * Define the target here: 
 * (Use only ONE of them! Never both!) 
 */ 
#define RP6BASE 
//#define RP6CONTROL 

/**
 * Define the PCF8574 I2C address here:
 */
#define PCF8574_LCD_ADR  0x70


#endif
Demo

makefile:

...
TARGET = RP6_I2ClcdTWI
...
SRC += $(RP6_LIB_PATH)/RP6base/RP6RobotBaseLib.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6uart.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6I2CmasterTWI.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6I2ClcdTWI.c
...

Datei RP6_I2ClcdTWI.c:

/* 
 * ****************************************************************************
 * RP6 ROBOT SYSTEM - ROBOT BASE EXAMPLES
 * ****************************************************************************
 * Example: I2C-LCD and Serial Interface Example
 * Author(s): Dirk
 * ****************************************************************************
 * Description:
 * A more complex "Hello World" program for RP6v2.
 * It writes text to the PC with the serial interface
 * and shows a moving text on the I2C-LCD.
 *
 * ############################################################################
 * The Robot does NOT move in this example! You can simply put it on a table
 * next to your PC and you should connect it to the PC via the USB Interface!
 * ############################################################################
 * ****************************************************************************
 */

/*****************************************************************************/
// Includes:

#include "RP6RobotBaseLib.h"	// The RP6 Robot Base Library.
				// Always needs to be included!
#include "RP6I2CmasterTWI.h"	// Include the I2C-Bus Master Library

#include "RP6I2ClcdTWI.h"	// Include the I2C-LCD Library

/*****************************************************************************/
// I2C

/**
 * This function gets called automatically if there was an I2C Error like
 * the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30).
 * The most common mistakes are: 
 *   - using the wrong address for the slave
 *   - slave not active or not connected to the I2C-Bus
 *   - too fast requests for a slower slave
 * Be sure to check this if you get I2C errors!
 */
void I2C_transmissionError(uint8_t errorState)
{
	writeString_P("\nI2C ERROR - TWI STATE: 0x");
	writeInteger(errorState, HEX);
	writeChar('\n');
}

/*****************************************************************************/

/**
 * Two running I2C-LCD texts with Stopwatch2 and 400ms refresh rate:
 */
void task_runI2CLCDText(void)
{
	static uint8_t cnt = 0; // A counting variable for text position
	static uint8_t dir = 0; // Moving direction for I2C-LCD text
	
	if(getStopwatch2() > 400) // 400ms
	{
		// Hint: This is just a simple example and you need to adjust 
		// some parameters if you want to change the moving text! 
		// Like it is now - both strings must have 5 characters!

		// Overwrite the two previous Strings:
		clearPosI2CLCD(0, cnt, 5);
		clearPosI2CLCD(1, (11 - cnt), 5);

		// Increase/Decrease the cursor position depending on
		// direction.
		if(dir == 0)
			cnt++;
		else
			cnt--;
		// Change the direction:
		if(cnt > 10)
			dir = 1;
		if(cnt < 1)
			dir = 0;

		// Write two text strings to the I2C-LCD...
		// ... the first starts at the first position on the LCD:
		setCursorPosI2CLCD(0, cnt); // Sets cursor position on the display
		writeStringI2CLCD_P("HELLO"); // Writes a string from flash to the
		// I2C-LCD ... and the second is on the second line and starts at the 
		// last position.
		setCursorPosI2CLCD(1, (11 - cnt) ); 
		writeStringI2CLCD_P("WORLD");

		// Reset Stopwatch #2:
		setStopwatch2(0);
	}
}

/*****************************************************************************/
// Main function - The program starts here:

int main(void)
{
	initRobotBase();	// Always call this first! The Processor will not work
				// correctly otherwise.

	I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation
				// with 100kHz SCL Frequency
				// PCF8574 and PCF8591 are only specified for
				// up to 100kHz SCL freq - not 400kHz HighSpeed mode!
	
	// Register the event handlers:
	I2CTWI_setTransmissionErrorHandler(I2C_transmissionError);

	initI2CLCD();	// Initialize the I2C-LC-Display (I2C-LCD)
			// Always call this before using the I2C-LCD!

	// ---------------------------------------
	// Write messages to the Serial Interface
	// (here it is a RP6v2 text logo):
	writeString_P("\n\n   _______________________\n");
	writeString_P("   \\| RP6  ROBOT SYSTEM |/\n");
	writeString_P("    \\_-_-_-_-_-_-_-_-_-_/\n\n");

	writeString_P("RP6v2 Base. I2C-LC-DISPLAY.\n");
	writeString_P("Hello World! Example Program!\n");
	writeString_P("Let's go! :)\n");

	setLEDs(0b111111);
	mSleep(500);	   
	setLEDs(0b000000);
	
	powerON();
	
	showScreenI2CLCD("################", "################");
	mSleep(1500);
	showScreenI2CLCD("<< RP6v2 Base >>", "<I2C-LC-DISPLAY>");
	mSleep(2500); 
	showScreenI2CLCD("Hello World", "Example Program");
	mSleep(2500);
	clearI2CLCD(); // Clear the whole I2C-LCD screen
	
	startStopwatch2();
	
	while(true) 
	{
		task_runI2CLCDText();
		task_I2CTWI();
		task_RP6System();
	}
	return 0;
}

RP6 CONTROL M32

Config

Die Konfigurations-Datei der RP6v2 I2C-LCD Library gehört in den jeweiligen Programmordner.

Datei RP6I2ClcdTWIConfig.h:

/**
 * Settings for the RP6I2ClcdTWI Library
 */ 

#ifndef RP6I2CLCDCONFIG_H
#define RP6I2CLCDCONFIG_H


/**
 * Define the target here: 
 * (Use only ONE of them! Never both!) 
 */ 
//#define RP6BASE 
#define RP6CONTROL 

/**
 * Define the PCF8574 I2C address here:
 */
#define PCF8574_LCD_ADR  0x70


#endif
Demo

makefile:

...
TARGET = RP6_I2ClcdTWI
...
SRC += $(RP6_LIB_PATH)/RP6control/RP6ControlLib.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6uart.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6I2CmasterTWI.c
SRC += $(RP6_LIB_PATH)/RP6common/RP6I2ClcdTWI.c
...

Datei RP6_I2ClcdTWI.c:

/* 
 * ****************************************************************************
 * RP6 ROBOT SYSTEM - RP6 CONTROL M32 Examples
 * ****************************************************************************
 * Example: I2C-LCD and Serial Interface Example
 * Author(s): Dirk
 * ****************************************************************************
 * Description:
 * A more complex "Hello World" program for the RP6 CONTROL M32.
 * It writes text to the PC with the serial interface
 * and shows a moving text on the I2C-LCD.
 *
 * ############################################################################
 * The Robot does NOT move in this example! You can simply put it on a table
 * next to your PC and you should connect it to the PC via the USB Interface!
 * ############################################################################
 * ****************************************************************************
 */

/*****************************************************************************/
// Includes:

#include "RP6ControlLib.h" 	// The RP6 Control Library. 
				// Always needs to be included!
#include "RP6I2CmasterTWI.h"	// Include the I2C-Bus Master Library

#include "RP6I2ClcdTWI.h"	// Include the I2C-LCD Library

/*****************************************************************************/
// I2C

/**
 * This function gets called automatically if there was an I2C Error like
 * the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30).
 * The most common mistakes are: 
 *   - using the wrong address for the slave
 *   - slave not active or not connected to the I2C-Bus
 *   - too fast requests for a slower slave
 * Be sure to check this if you get I2C errors!
 */
void I2C_transmissionError(uint8_t errorState)
{
	writeString_P("\nI2C ERROR - TWI STATE: 0x");
	writeInteger(errorState, HEX);
	writeChar('\n');
}

/*****************************************************************************/

/**
 * Two running I2C-LCD texts with Stopwatch2 and 400ms refresh rate:
 */
void task_runI2CLCDText(void)
{
	static uint8_t cnt = 0; // A counting variable for text position
	static uint8_t dir = 0; // Moving direction for I2C-LCD text
	
	if(getStopwatch2() > 400) // 400ms
	{
		// Hint: This is just a simple example and you need to adjust 
		// some parameters if you want to change the moving text! 
		// Like it is now - both strings must have 5 characters!

		// Overwrite the two previous Strings:
		clearPosI2CLCD(0, cnt, 5);
		clearPosI2CLCD(1, (11 - cnt), 5);

		// Increase/Decrease the cursor position depending on
		// direction.
		if(dir == 0)
			cnt++;
		else
			cnt--;
		// Change the direction:
		if(cnt > 10)
			dir = 1;
		if(cnt < 1)
			dir = 0;

		// Write two text strings to the I2C-LCD...
		// ... the first starts at the first position on the LCD:
		setCursorPosI2CLCD(0, cnt); // Sets cursor position on the display
		writeStringI2CLCD_P("HELLO"); // Writes a string from flash to the
		// I2C-LCD ... and the second is on the second line and starts at the 
		// last position.
		setCursorPosI2CLCD(1, (11 - cnt) ); 
		writeStringI2CLCD_P("WORLD");

		// Reset Stopwatch #2:
		setStopwatch2(0);
	}
}

/*****************************************************************************/
// Main function - The program starts here:

int main(void)
{
	initRP6Control();	// Always call this first! The Processor will not work
				// correctly otherwise. 

	I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation
				// with 100kHz SCL Frequency
				// PCF8574 and PCF8591 are only specified for
				// up to 100kHz SCL freq - not 400kHz HighSpeed mode!
	
	// Register the event handlers:
	I2CTWI_setTransmissionErrorHandler(I2C_transmissionError);

	initI2CLCD();	// Initialize the I2C-LC-Display (I2C-LCD)
			// Always call this before using the I2C-LCD!

	// ---------------------------------------
	// Write messages to the Serial Interface
	// (here it is a RP6 text logo):
	writeString_P("\n\n   _______________________\n");
	writeString_P("   \\| RP6  ROBOT SYSTEM |/\n");
	writeString_P("    \\_-_-_-_-_-_-_-_-_-_/\n\n");

	writeString_P("RP6 Control M32. I2C-LC-DISPLAY.\n");
	writeString_P("Hello World! Example Program!\n");
	writeString_P("Let's go! :)\n");

	setLEDs(0b1111);
	mSleep(500);
	setLEDs(0b0000);
	
	showScreenI2CLCD("################", "################");
	mSleep(1500);
	showScreenI2CLCD("RP6 Control M32 ", "<I2C-LC-DISPLAY>");
	mSleep(2500); 
	showScreenI2CLCD("Hello World", "Example Program");
	mSleep(2500);
	clearI2CLCD(); // Clear the whole I2C-LCD screen
	
	startStopwatch2();
	
	while(true) 
	{
		task_runI2CLCDText();
		task_I2CTWI();
	}
	return 0;
}

RP6 CCPRO M128

Datei RP6_I2Clcd.cc:

/*******************************************************************************
 * RP6 C-Control PRO M128 Erweiterungsmodul
 * ----------------------------------------------------------------------------
 * I2C-LCD und Serielle Schnittstelle Beispielprogramm
 * ----------------------------------------------------------------------------
 * Ein komplexeres "Hallo Welt" Programm für die RP6 C-Control PRO M128.
 * Dieses Programm gibt einen Text auf der seriellen Schnittstelle aus und
 * zeigt einen sich bewegenden Text auf dem I2C-LCD (sofern vorhanden).
 *
 * Hinweis:
 * Für allgemeine Programmierhinweise zur CCPRO schauen Sie sich bitte die
 * CCPRO Dokumentation und die zugehörigen Beispielprogramme an!
 * Es sei auch nochmals darauf hingewiesen, dass die normalen Beispielprogramme
 * für die CCPRO nur zum Teil direkt auf dem RP6-CCPRO Modul laufen.
 * Die Anschlussbelegung ist natürlich komplett anders als auf dem
 * C-Control Application Board. Auch die LCD Ansteuerung ist leicht anders,
 * da hier noch zusätzliche LEDs mit an dem Schieberegister angeschlossen sind.
 * --> Die C-Control Beispielprogramme müssen ein klein wenig angepasst
 * werden bevor sie auf diesem Modul laufen können!
 *
 * *****************************************************************************
 * Der Roboter bewegt sich in diesem Beispielprogramm NICHT und kann
 * am Rechner angeschlossen bleiben!
 ******************************************************************************/

// WICHTIG: Immer die RP6CCLib mit einbinden:
#include "../../RP6CCLib/RP6CCLib.cc"

// I2C Bus Adresse des PCF8574:
#define PCF8574_LCD_ADR 0x70

// Die neue RP6I2ClcdCCLib einbinden:
#include "../../RP6CCLib/RP6I2ClcdCClib.cc"

int irqcnt;                                 // globale Variablendeklaration
byte cnt1;

/******************************************************************************/

/**
 * Zwei laufende I2C-LCD Texte mit 400ms Wiederhol-Rate:
 */
void runI2CLCDText(void)
{
	static byte cnt; // Eine Zählvariable für die Textposition
	static byte dir; // Bewegungsrichtung für den I2C-LCD Text

	if(cnt1 > 40) // 400ms
	{
		clearPosI2CLCD(0, cnt, 5);
		clearPosI2CLCD(1, (11 - cnt), 5);

		if(dir == 0)
			cnt++;
		else
			cnt--;

		if(cnt > 10)
			dir = 1;
		if(cnt < 1)
			dir = 0;

		setCursorPosI2CLCD(0, cnt);
		writeStringI2CLCD("HALLO");

		setCursorPosI2CLCD(1, (11 - cnt) );
		writeStringI2CLCD("WELT!");

		cnt1 = 0;
    }
}

/******************************************************************************/
void main(void)
{
    // WICHTIG! Immer als erstes aufrufen:
    RP6_CCPRO_Init(); // Auf Startsignal warten, LCD und andere Dinge initialisieren !

    // Immer aufrufen, bevor das I2C-LCD benutzt wird:
    RP6_initI2CLCD(); // Das I2C-LCD initialisieren

    Irq_SetVect(INT_TIM2COMP, INT_10ms);    // ISR definieren
                                            // Timer2: 10ms Interrupt

    // ------------------------------------------------------

    // Text über serielle Schnittstelle ausgeben:
    newline(); // Neue Zeile
    println("   ________________________");
    println("   \\| RP6  ROBOT SYSTEM |/");
    println("    \\_-_-_-_-_-_-_-_-_-_/ ");
    newline();
    println("RP6 C-Control M128. I2C-LC-DISPLAY.");
    newline();
    println("Hallo Welt! Beispielprogramm!");
    newline();
    println("Let's go!");
    newline();

    // 0,5 Sekunden Pause:
    AbsDelay(500);

    // Zwei Zeilen Text mit dem I2C-LCD anzeigen:
    showScreenI2CLCD("################", "################");
    AbsDelay(1500);
    showScreenI2CLCD(" RP6 CCPRO M128 ", "<I2C-LC-DISPLAY>");
    AbsDelay(2500);
    showScreenI2CLCD("Hallo Welt", "Beispielprogramm");
    AbsDelay(2500);
    clearI2CLCD(); // Lösche die ganze I2C-LCD Anzeige

    // ------------------------------------------------------

    while(1)
    {
        runI2CLCDText();
    }
}

//------------------------------------------------------------------------------
// Interrupt Routine
//
void INT_10ms(void)
{
    cnt1++;                                 // Zähler für die Wiederhol-Rate

    irqcnt = Irq_GetCount(INT_TIM2COMP);    // Interrupt Request Counter
}

//------------------------------------------------------------------------------

I/O-Port-Demos

Wenn mit dem PCF8574 nicht ein LC-Display angesteuert werden soll, kann man die 8 I/O-Ports einzeln als Ein- oder Ausgänge verwenden. Es gibt beim PCF8574 kein "Datenrichtungsregister" (DDRx), wie bei den Atmel-Microcontrollern. Die I/O-Ports sind "quasi-bidirektionale I/Os", die praktisch "gleichzeitig" Ein- und Ausgang sind. Mit I2C-Schreib-Befehlen können die I/O-Ports auf High- oder Lowpegel geschaltet werden. Mit I2C-Lese-Befehlen können die Eingangspegel an den I/O-Ports ausgelesen werden. Sinnvoll kann man nur die I/O-Ports auslesen, die vorher mit einem I2C-Schreib-Befehl auf Highpegel (1) geschaltet wurden.

RP6v2 Base und CONTROL M32

Die Demo "RP6Base_I2CMaster_01.c" aus den RP6v2 Examples zeigt, wie die 8 I/O-Ports des PCF8574 als Ausgänge genutzt werden.

Beispiel:

#define PCF8574_IO_ADR  0x70
uint8_t outByte;

I2CTWI_transmitByte(PCF8574_IO_ADR, outByte);

Über den I2C-Bus wird outByte an einen PCF8574AP mit der I2C-Adresse 0x70 gesendet. Der PCF8574 schaltet danach die 8 I/O-Ports entsprechend dem Inhalt von outByte. Hat outByte z.B. den Wert 112 (binär 01110000), dann werden die I/O-Ports P4..P6 Highpegel (1), die restlichen I/O-Ports Lowpegel (0) führen. Diese Pegel bleiben so lange erhalten, bis ein neuer Schreib-Befehl über den I2C-Bus eingeht.


Will man alle I/O-Ports des PCF8574 als Eingänge nutzen, müssen sie zunächst auf Highpegel (1) geschaltet werden (direkt nach dem Anlegen der Betriebsspannung ist das jedoch nicht erforderlich!):

I2CTWI_transmitByte(PCF8574_IO_ADR, 255);

Das Einlesen der I/O-Ports erfolgt dann so:

uint8_t inByte;

inByte = I2CTWI_readByte(PCF8574_IO_ADR);

Es ist möglich, nicht alle I/O-Ports des PCF8574 gleichzeitig als Ein- oder Ausgänge zu verwenden. Die I/O-Ports, die als Eingänge dienen sollen, müssen bei allen I2C-Schreib-Befehlen auf Highpegel (1) bleiben,- deren Pegel können dann mit I2C-Lese-Befehlen eingelesen werden.

Interrupt

Der PCF8574 hat einen Ausgang INT, mit dem der Portexpander einem Microcontroller mitteilen kann, dass sich der Pegel an einem seiner I/O-Ports geändert hat. Dazu muss es zusätzlich zum I2C-Bus eine Verbindung zu einem Microcontroller-Portpin geben. Bei der hier vorgestellten I2C-Portexpander-Platine kann der INT-Ausgang des PCF8574 über Jumper JP4 mit INT1 des XBUS (Pin 8) des RP6-Systems verbunden werden. Das bedeutet, dass INT des PCF8574 auf allen Microcontroller-Plattformen (Base, CONTROL M32 und CCPRO M128) des RP6-Systems ausgewertet werden kann. Sinnvoll ist, dass der jeweilige I2C-Master die Auswertung des INT-Signals des PCF8574 vornimmt. Er kann über den I2C-Bus Daten vom Portexpander einlesen, wenn dieser angezeigt hat, dass sich Eingangspegel verändert haben. Der PCF8574 zeigt über den INT-Ausgang innerhalb von max. 4 us diese Veränderung an, indem INT auf Lowpegel (0) wechselt. Normalerweise liegt dieser Ausgang INT durch den Pullup-Widerstand 2,2 kOhm (trotz des 10 kOhm Pulldown-Widerstands R34 auf dem RP6v2 Mainboard) auf Highpegel (1).

Wenn die RP6v2 Base I2C-Master ist, kann das INT-Signal mit deren Portpin PA4, mit der CONTROL M32 als I2C-Master mit deren Portpin PD2 ausgewertet werden.

Muss ein I2C-Master also rasch auf Pegeländerungen des Portexpanders reagieren, fragt er regelmäßig den jweiligen Portpin auf Lowpegel ab ("polling") oder reagiert mit einem Interrupt auf eine High-Low-Flanke an dem Portpin (wenn dieser Interrupt-fähig ist, wie PD2 (= INT0) der CONTROL M32!). Wird ein solches Ereignis erkannt, kann der I2C-Master anschließend die Daten über den I2C-Bus einlesen.

Der PCF8574 setzt seinen Ausgang INT unter folgenden Bedingungen wieder zurück auf Highpegel (1) und reagiert damit auf erneute Pegeländerungen:

  • Die Eingangspegel der I/O-Ports wechseln wieder zum vorherigen Zustand
  • Es erfolgt ein I2C-Lese-Zugriff auf den Port
  • Es erfolgt ein I2C-Schreib-Zugriff auf den Port

RP6 CCPRO M128

Die Ausgabe eines Werts an den PCF8574 erfolgt in ähnlicher Weise auch mit CC (siehe auch weiter oben die identische Funktion void RP6_writeByte(byte adr, byte data) der RP6I2ClcdCC Library!):

Thread_Lock(1);
I2C_Start();
I2C_Write(PCF8574_IO_ADR);
I2C_Write(outByte);
I2C_Stop();
Thread_Lock(0);

Auch das Einlesen der I/O-Ports gelingt mit CC:

Thread_Lock(1);
I2C_Start();
I2C_Write(PCF8574_IO_ADR + 1);
inByte = I2C_Read_NACK();
I2C_Stop();
Thread_Lock(0);
Interrupt

Wie weiter oben für die Base und CONTROL M32 beschrieben, kann auch die CCPRO M128 den INT-Ausgang des PCF8574 auswerten, weil er über INT1 des XBUS (Pin 8) auch auf dieser Erweiterungsplatine verfügbar ist.

Die CCPRO M128 als I2C-Master kann das INT-Signal mit ihrem Portpin PE5 (= INT5) auswerten. Da dieser Portpin auch Interrupt-fähig ist, ist ein zeitnahes Einlesen der Daten des Portexpanders über den I2C-Bus möglich.


Phase 2

ADC-Demos

RP6v2 Base und CONTROL M32

RP6 CCPRO M128

DAC-Demos

RP6v2 Base und CONTROL M32

RP6 CCPRO M128

Siehe auch

Quellen


Autoren

--Dirk 18:25, 06. Juni 2012 (CET)


LiFePO4 Speicher Test