Når man ligesom jeg kun leger med embedded systemer og hardware i Januar/Februar er det en god ide at standardisere sig på nogle standarder der ikke udvikler sig. F.ex så er det meget frustrerende når den nye avr-gcc ikke kan compilere de programmer jeg lavede året før. Det er både fordi compilieren bliver mere strikt men ikke mindst fordi AVR Atmega er Harward architecture, hvorimod gcc er designed for Van Neuman verdenen, og de dygtige compiler folk vil gerne flytte sig højere og højere op i abstraktions niveau. Det har jeg absolut ingen interesse i (læs: mangler evnerne).
Mht til CPU så bruger jeg hvad som helst jeg nu har. For tiden er det AVR atmega, TI msp430, TI CC1110Fx/CC1111Fx (8051). Jeg foretrækker Van Neuman CPU-er, men specielt i den embeddede verden synes nogen der kan være et godt argument for Harward CPU-er, FLASH/ROM/EEPROM til code og RAM til data. Vil man have data i FLASH, eller code i RAM er man ??cked. Hvad er det nu lige der er galt med selvmodificerende kode?
Mht til programmerings-sprog, så er det gammeldags C, “gcc” eller “sdcc”. Til test og debugning tager jeg inspiration fra FORTH, forstået på den måde at jeg ingen ønske har om at lave et FORTH system, men hvis jeg laver mine små-programmer således at de opfører sig som FORTH-words, så er det nemt at teste interaktivt, og systemet kan se ens ud, selv om man flytter til ny CPU, og det er nemt at lime forskellige FORTH-words sammen uden at skulle recompilere det hele, og på Harward CPU-er kan man så have code i RAM/EEPROM også.
Mht til I/O grænseflade så vil jeg helst gå via i2c eller Dallas One-Wire, så jeg uden alt for meget besvær kan skifte Processor-platformen ud. og f.ex lave udvikling direkte på en laptop, og senere flytte det ned på en Embedded-SoC (System on a Chip) eller arduino.
Linux har god support for både i2c og one-wire, så man kan faktisk nå langt bare ved hjælp af kommando-linien og Shell-programering. Det kan jeg li’
På lang de flest Linux SoC systemer kan der altid lige findes et par GPIO pins så man kan forbinde en i2c-bus, Her er lidt inspiration:
- 1-Wire on Raspberry Pi – Monitoring my Heating – Hardware
http://www.astounding.org.uk/ian/raspi-1wire - Dockstar Pinout SDHC Card and I2C
http://archlinuxarm.org/forum/viewtopic.php?f=6&t=258#p1748 - Marvell Kirkwood SoC pinctrl driver for mpp
http://free-electrons.com/kerneldoc//latest/devicetree/bindings/pinctrl/marvell,kirkwood-pinctrl.txt
Jeg foretrækker at lege med i2c via en usb-to-i2c-adapter, og sådan en kan man lave selv for små penge, dvs man kan køber en USBASP formedelst $2.28 leveret fra ebay.com, og så får den en ny firmware, det er det der skal ske i denne POST:
USBASP med I2C-TINY_USB firmware
USBASP-programmer bruges til at putte kode ind i AVR atmega cpu-er. USBASP er udviklet af Thomas Fischl, og selv baseret på en atmega8, og både hardware, firmware og source-code er tilgængelig under GPL-2 licens. http://www.fischl.de/usbasp
Till Harbaum har æren for “i2c-tiny-usb” http://www.harbaum.org/till/i2c_tiny_usb, som er en lille AVR tiny45/atmega8 som gør i2c tilgængelig via USB. Det bedste ved denne løsning er at den er supporteret under Linux, så en i2c-enhed forbundet via en i2c-tiny-usb håndteres præcis som de andre i2c-enheder der måtte være i din Linux-box, og der er drivere til det meste.
Så går vi igang – Vi skal have krydset hardware fra USBASP med firmwaren fra i2c-tiny-usb
Først skal vi lige hente lidt remedier fra
http://www.atmel.com/tools/ATMELAVRTOOLCHAINFORLINUX.aspx, på min ubuntu skal jeg bruge 64bit versionen.
$ tar xvzf DIST/avr8-gnu-toolchain-3.4.5.1522-linux.any.x86_64.tar.gz $ ls -l `pwd`/avr8-gnu-toolchain-linux_x86_64/bin $ export PATH=`pwd`/avr8-gnu-toolchain-linux_x86_64/bin:$PATH $ hash -r $ type -a avr-gcc
Hent http://www.harbaum.org/till/i2c_tiny_usb/i2c_tiny_usb-2009-02-10.zip den skal pakkes den ud, og tilpasses USBASP hardwaren
$ unzip DIST/i2c_tiny_usb-2009-02-10.zip $ patch -p0 << EOF --- i2c_tiny_usb-2009-02-10/firmware/main.c 2007-06-07 09:53:47.000000000 -0400 +++ i2c_tiny_usb/firmware/main.c 2015-02-01 13:11:07.676250008 -0500 @@ -166,9 +166,15 @@ static unsigned char saved_cmd; #if! defined (__AVR_ATtiny45__) +#ifdef USBASP // MISO as SDA, SCLK as SCL +#define I2C_PORT PORTB +#define I2C_PIN PINB +#define I2C_DDR DDRB +#else #define I2C_PORT PORTC #define I2C_PIN PINC #define I2C_DDR DDRC +#endif #define I2C_SDA _BV(4) #define I2C_SCL _BV(5) #else --- i2c_tiny_usb-2009-02-10/firmware/Makefile-avrusb.mega8 2006-12-03 16:28:59.000000000 -0500 +++ i2c_tiny_usb/firmware/Makefile-avrusb.mega8 2015-02-01 14:24:07.262767528 -0500 @@ -14,8 +14,8 @@ # to a Keyspan USB to serial converter to a Mac running Mac OS X. # Choose your favorite programmer and interface. -DEFINES += -DDEBUG -DEFINES += -DDEBUG_LEVEL=1 +#DEFINES += -DDEBUG -DDEBUG_LEVEL=1 +DEFINES += -DUSBASP -DF_CPU=12000000 COMPILE = avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega8 $(DEFINES) OBJECTS = usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o main.o --- i2c_tiny_usb-2009-02-10/firmware/usbconfig.h 2007-05-19 08:30:11.000000000 -0400 +++ i2c_tiny_usb/firmware/usbconfig.h 2015-02-01 14:25:35.646071082 -0500 @@ -19,6 +19,12 @@ /* ---------------------------- Hardware Config ---------------------------- */ +#ifdef USBASP +#define USB_CFG_IOPORTNAME B +#define USB_CFG_DMINUS_BIT 0 +#define USB_CFG_DPLUS_BIT 1 +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) +#else #if! defined (__AVR_ATtiny45__) #define USB_CFG_IOPORTNAME C /* This is the port where the USB bus is connected. When you configure it to @@ -39,6 +45,7 @@ #define USB_CFG_DMINUS_BIT 0 #define USB_CFG_DPLUS_BIT 2 #endif +#endif /* --------------------------- Functional Range ---------------------------- */ EOF $ cd i2c_tiny_usb/firmware $ make -f Makefile-avrusb.mega8
Hvis din avr-gcc er af nyere dato gik det IKKE, og du kan enten tilføje en masse “const” der hvor den brokker sig, eller hente en nyere version af V-USB firmware.
$ tar xvzf ../../DIST/vusb-20121206.tar.gz $ rm -rf usbdrv $ ln -s vusb-20121206/usbdrv . $ make -f Makefile-avrusb.mega8
Denne gang var det sikkert OK, medmindre der er kommet en ny avr-gcc siden jeg skrev dette.
Så skal de to USBASP-er forbindes sammen, og den der skal blive til i2c-tiny-usb, skal have en jumper i jp2 (RESET), og så der kan brændes ny firmware
$ make program
Det var det hele, nu kan vi forbinde en i2c enhed til vores Linux box, via vores nyprogrammerede USBASP, lad os se om det virker, vi forbinder en $0.99 i2c-lcd-adapter fra ebay den er baseret på en pcf8574.
$ lsusb Bus 001 Device 012: ID 0403:c631 Future Technology Devices International, Ltd i2c-tiny-usb interface Bus 001 Device 006: ID 16c0:05dc Van Ooijen Technische Informatica shared ID for use with libusb Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. 4-Port HUB Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub $ i2cdetect -l i2c-0 i2c i915 gmbus ssc I2C adapter i2c-1 i2c i915 gmbus vga I2C adapter i2c-2 i2c i915 gmbus panel I2C adapter i2c-3 i2c i915 gmbus dpc I2C adapter i2c-4 i2c i915 gmbus dpb I2C adapter i2c-5 i2c i915 gmbus dpd I2C adapter i2c-6 i2c i2c-tiny-usb at bus 001 device 012 I2C adapter $ i2cdetect -y 6 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
Vores i2c-tiny-usb blev fundet, og “forbundet” som i2c bus nr. 6 på den her PC, og pcf8574 blev fundet på addresse 0x27 på den.
Nu skal vi blot have skrevet “Hello World” på LCD displayet, det er der andre der har gjort før os, så det benytter vi os af:
- PCF8574 i2c Driver Module for 1602 LCD Displays
telecnatron.com/modules/pcf8574-i2c-lcd-driver/ - Utility To Control 1602 LCD On Raspberry Pi Via A PCF8574 I2C Backpack Module
telecnatron.com/articles/Utility-To-Control-1602-LCD-On-Raspberry-Pi-Via-A-PCF8574-I2C-Backpack-Module/index.html
Vi henter source-koden ovenfor, og retter den til så den ikke er Rapsberry Pi specifik.
$ mkdir lcdi2c $ cd lcdi2c $ tar -xvzf DIST/lcdi2c-tar.gz $ patch -p0 << EOF --- lcdi2c.orig/lcd_i2c.c 2014-10-09 19:08:29.000000000 -0400 +++ lcdi2c/lcd_i2c.c 2015-02-01 10:46:05.155659267 -0500 @@ -4,8 +4,14 @@ // ---------------------------------------------------------------- #include "lcd_i2c.h" -#include <wiringPi.h> -#include <wiringPiI2C.h> +#define delayMicroseconds usleep +#define delay(ms) usleep(1000*ms) +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <linux/i2c-dev.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -19,7 +25,7 @@ // convenience macros // write current value of output variable to device -#define LCD_I2C_WRITE(lcd_p) wiringPiI2CWrite(lcd_p->fd, lcd_p->output); +#define LCD_I2C_WRITE(lcd_p) i2c_smbus_write_byte(lcd_p->fd, lcd_p->output) // enable #define LCD_I2C_E_HI(lcd_p) lcd_p->output |= (1<<LCD_I2C_E) #define LCD_I2C_E_LO(lcd_p) lcd_p->output &=~ (1<<LCD_I2C_E) @@ -87,8 +93,14 @@ int lcd_i2c_setup( lcd_i2c_t *lcd,int address) { + char device[100] ; + extern int8_t opt_bus; // global variable + sprintf(device, "/dev/i2c-%d", opt_bus); + if ((lcd->fd = open (device, O_RDWR)) < 0) + return -1 ; + if (ioctl (lcd->fd, I2C_SLAVE, address) < 0) + return -1 ; lcd->output=0; - lcd->fd=wiringPiI2CSetup(address); // XXX for now, alway use a 1602 display lcd->rows=2; lcd->cols=16; --- lcdi2c.orig/lcdi2c.c 2014-10-09 19:09:51.000000000 -0400 +++ lcdi2c/lcdi2c.c 2015-02-01 10:49:24.211071181 -0500 @@ -15,6 +15,7 @@ // option flags int8_t opt_cursor=0; // 0 off, 1 on , 2 blink int8_t opt_address = LCD_I2C_PCF8574_ADDRESS_DEFAULT; +int8_t opt_bus = 6; // global variable int8_t opt_backlight=-1; // -1 do nothing, 0 turn off, 1 turn on int8_t opt_clear; int8_t opt_cols = 16; @@ -109,6 +110,7 @@ char msg[]="Display string on a HD44780 LCD which is connected by the i2c bus via a PCF8574 port expander\n\ Options:\n\ \t-a address\t- use this i2c (hexidecimal) address (default 0x27)\n\ +\t-d bus\t- use this i2c bus (default 6)\n\ \t-i\t\t- initialise (reset) the lcd\n\ \t-r rows\t\t- set the number of rows (default 2)\n\ \t-c cols\t\t- set the number of columns (default 16)\n\ @@ -148,8 +150,11 @@ int option; opterr=0; // no error message printing by getopt() - while( (option = getopt(argc, argv,"ilha:r:c:x:y:b:s:")) != -1) { + while( (option = getopt(argc, argv,"ilha:d:r:c:x:y:b:s:")) != -1) { switch (option) { + case 'd': opt_bus=str2int8('s',optarg,10,0,127); + if(opt_bus<0) return -1; + break; case 'h' : opt_help = 1; break; case 'l': opt_clear=1; EOF $ make
Så er den oversat, lad os se om det virker?
$ ./lcdi2c --help Unknown option `--'. Usage: lcdi2c [options] "string to display" Display string on a HD44780 LCD which is connected by the i2c bus via a PCF8574 port expander Options: -a address - use this i2c (hexidecimal) address (default 0x27) -d bus - use this i2c bus (default 6) -i - initialise (reset) the lcd -r rows - set the number of rows (default 2) -c cols - set the number of columns (default 16) -x col - move cursor to this column (default 0) -y row - move cursor to this row (default 0) -b [01] - turn backlight on (1) or off (0) (default off) -l - clear the screen -s [012] - turn cursor off (0), on (1), or blink (2) (default off) -h - display this help message $ ./lcdi2c -i -d 6 -a 27 "Hello World"
Det virkede – og det var måske heldigt da de her i2c-lcd enheder kommer i to versioner (b0..b7) = (RS,RW,E,BL,d4..d7) eller (d4..d7,RS,RW,E,BL). Her bruger vi den første version men f.ex. LCDd er kodet til den anden version, det kan nu nemt fixes, enten i hardware eller i software.
$2.28 + $0.99 + $1.74
Godt og vel $5 og du kan kan have en 16×2 LCD display på din Linux – Det er da til at overskue.
You must be logged in to post a comment.