Sunday, September 14, 2014

Arduino Programming

It is interesting to me to note the minimum application size given the Arduino development environment.  I created this minimum sketch:

void setup() 
{
  // put your setup code here, to run once:
}

void loop() 
{
  // put your main code here, to run repeatedly:
}

The development environment reported the following for my Arduino UNO R3.

Sketch uses 466 bytes (1%) of program storage space. Maximum is 32,256 bytes.

Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables. Maximum is 2,048 bytes.

Interesting...  Using avr-size to report the same information:

   text    data     bss     dec     hex filename
    466       0       9     475     1db sketch_sep14a.cpp.elf

So this sketch that does nothing requires 466 bytes of code (start-up/shut-down) and 9 bytes of initialized memory.

Using obj-dump to look at the symbol table and different blocks, we find pretty typical initialization of vector tables, copying initialization data to RAM, default handlers and support for debugging in different modes.  

Too bad the Arduino IDE doesn't actually provide a debugger, but nice it is there if you want to step outside of the Arduino IDE (which I do recommend).

The program main entry point on Arduino is still main() like in any C program, but it is provided for you by the IDE.  As can be seen below, it calls the init function (also provided), then your setup followed by your loop function.  Upon return, the call stack frame pointer R28:r29 is examined and if non-zero, the device is reset through the vector at address zero.  Otherwise it just continues to call your loop function.


000000a6 <setup>:
  a6: 08 95        ret

000000a8 <loop>:
  a8: 08 95        ret

000000aa <main>:
  aa: cf 93        push r28
  ac: df 93        push r29
  ae: 0e 94 ac 00  call 0x158 ; 0x158 <init>
  b2: 0e 94 53 00  call 0xa6 ; 0xa6 <setup>
  b6: c0 e0        ldi r28, 0x00 ; 0
  b8: d0 e0        ldi r29, 0x00 ; 0
  ba: 0e 94 54 00  call 0xa8 ; 0xa8 <loop>
  be: 20 97        sbiw r28, 0x00 ; 0
  c0: e1 f3        breq .-8       ; 0xba <main+0x10>
  c2: 0e 94 00 00  call 0 ; 0x0 <__vectors>
  c6: f9 cf        rjmp .-14      ; 0xba <main+0x10>


Explore some of the other tools that are provided with your Arduino development environment.  Here is most of the output from obj-dump with the big blocks of hex dumps and disassemblies of debugging blocks removed.


sketch_sep14a.cpp.elf:     file format elf32-avr
sketch_sep14a.cpp.elf
architecture: avr:5, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000

Program Header:
    LOAD off    0x00000074 vaddr 0x00000000 paddr 0x00000000 align 2**0
         filesz 0x000001d2 memsz 0x000001d2 flags r-x
    LOAD off    0x00000246 vaddr 0x00800100 paddr 0x00800100 align 2**0
         filesz 0x00000000 memsz 0x00000009 flags rw-

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000001d2  00000000  00000000  00000074  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .bss          00000009  00800100  00800100  00000246  2**0
                  ALLOC
  2 .debug_aranges 000000b0  00000000  00000000  00000246  2**0
                  CONTENTS, READONLY, DEBUGGING
  3 .debug_pubnames 000000cd  00000000  00000000  000002f6  2**0
                  CONTENTS, READONLY, DEBUGGING
  4 .debug_info   000005e4  00000000  00000000  000003c3  2**0
                  CONTENTS, READONLY, DEBUGGING
  5 .debug_abbrev 00000296  00000000  00000000  000009a7  2**0
                  CONTENTS, READONLY, DEBUGGING
  6 .debug_line   00000722  00000000  00000000  00000c3d  2**0
                  CONTENTS, READONLY, DEBUGGING
  7 .debug_frame  000000e0  00000000  00000000  00001360  2**2
                  CONTENTS, READONLY, DEBUGGING
  8 .debug_str    00000250  00000000  00000000  00001440  2**0
                  CONTENTS, READONLY, DEBUGGING
  9 .debug_loc    0000019b  00000000  00000000  00001690  2**0
                  CONTENTS, READONLY, DEBUGGING
 10 .debug_ranges 00000090  00000000  00000000  0000182b  2**0
                  CONTENTS, READONLY, DEBUGGING
SYMBOL TABLE:
00000000 l    d  .text 00000000 .text
00800100 l    d  .bss 00000000 .bss
00000000 l    d  .debug_aranges 00000000 .debug_aranges
00000000 l    d  .debug_pubnames 00000000 .debug_pubnames
00000000 l    d  .debug_info 00000000 .debug_info
00000000 l    d  .debug_abbrev 00000000 .debug_abbrev
00000000 l    d  .debug_line 00000000 .debug_line
00000000 l    d  .debug_frame 00000000 .debug_frame
00000000 l    d  .debug_str 00000000 .debug_str
00000000 l    d  .debug_loc 00000000 .debug_loc
00000000 l    d  .debug_ranges 00000000 .debug_ranges
00000084 l       .text 00000000 .do_copy_data_start
00000080 l       .text 00000000 .do_copy_data_loop
00000094 l       .text 00000000 .do_clear_bss_start
00000092 l       .text 00000000 .do_clear_bss_loop
00000000 l    df *ABS* 00000000 sketch_sep14a.cpp
0000003f l       *ABS* 00000000 __SREG__
0000003e l       *ABS* 00000000 __SP_H__
0000003d l       *ABS* 00000000 __SP_L__
00000034 l       *ABS* 00000000 __CCP__
00000000 l       *ABS* 00000000 __tmp_reg__
00000001 l       *ABS* 00000000 __zero_reg__
00000000 l    df *ABS* 00000000 main.cpp
0000003f l       *ABS* 00000000 __SREG__
0000003e l       *ABS* 00000000 __SP_H__
0000003d l       *ABS* 00000000 __SP_L__
00000034 l       *ABS* 00000000 __CCP__
00000000 l       *ABS* 00000000 __tmp_reg__
00000001 l       *ABS* 00000000 __zero_reg__
00000000 l    df *ABS* 00000000 wiring.c
0000003f l       *ABS* 00000000 __SREG__
0000003e l       *ABS* 00000000 __SP_H__
0000003d l       *ABS* 00000000 __SP_L__
00000034 l       *ABS* 00000000 __CCP__
00000000 l       *ABS* 00000000 __tmp_reg__
00000001 l       *ABS* 00000000 __zero_reg__
00800108 l     O .bss 00000001 timer0_fract
000001d0 l       .text 00000000 __stop_program
00000000 l    df *ABS* 00000000 hooks.c
0000003f l       *ABS* 00000000 __SREG__
0000003e l       *ABS* 00000000 __SP_H__
0000003d l       *ABS* 00000000 __SP_L__
00000034 l       *ABS* 00000000 __CCP__
00000000 l       *ABS* 00000000 __tmp_reg__
00000001 l       *ABS* 00000000 __zero_reg__
000000a2  w      .text 00000000 __vector_22
00800100 g     O .bss 00000004 timer0_overflow_count
000000a2  w      .text 00000000 __vector_1
00800104 g     O .bss 00000004 timer0_millis
00000068 g       .text 00000000 __trampolines_start
000001d2 g       .text 00000000 _etext
000000a2  w      .text 00000000 __vector_24
000000a8 g     F .text 00000002 loop
000000a2  w      .text 00000000 __vector_12
000000a2 g       .text 00000000 __bad_interrupt
000001d2 g       *ABS* 00000000 __data_load_end
000000a2  w      .text 00000000 __vector_6
00000068 g       .text 00000000 __trampolines_end
000000a2  w      .text 00000000 __vector_3
000000a2  w      .text 00000000 __vector_23
000001d2 g       *ABS* 00000000 __data_load_start
00000068 g       .text 00000000 __dtors_end
00800109 g       .bss 00000000 __bss_end
000000a2  w      .text 00000000 __vector_25
000000a2  w      .text 00000000 __vector_11
00000068  w      .text 00000000 __init
00000000  w      *UND* 00000000 _Z14serialEventRunv
000000a2  w      .text 00000000 __vector_13
000000a2  w      .text 00000000 __vector_17
000000a2  w      .text 00000000 __vector_19
000000a2  w      .text 00000000 __vector_7
0000008a g       .text 00000000 __do_clear_bss
00810000 g       .debug_aranges 00000000 __eeprom_end
00000000 g       .text 00000000 __vectors
00800100 g       .text 00000000 __data_end
00000000  w      .text 00000000 __vector_default
000000a2  w      .text 00000000 __vector_5
00000158 g     F .text 00000076 init
00000068 g       .text 00000000 __ctors_start
00000074 g       .text 00000000 __do_copy_data
00800100 g       .bss 00000000 __bss_start
000000aa g     F .text 0000001e main
000000a2  w      .text 00000000 __vector_4
00000000  w      *ABS* 00000000 __heap_end
000000a2  w      .text 00000000 __vector_9
000000a2  w      .text 00000000 __vector_2
000000a2  w      .text 00000000 __vector_21
000000a2  w      .text 00000000 __vector_15
000000a6 g     F .text 00000002 setup
00000068 g       .text 00000000 __dtors_start
00000068 g       .text 00000000 __ctors_end
000008ff  w      *ABS* 00000000 __stack
00800100 g       .text 00000000 _edata
00800109 g       .debug_aranges 00000000 _end
000000a2  w      .text 00000000 __vector_8
000001ce  w      .text 00000000 exit
000001ce g       .text 00000000 _exit
000000a2  w      .text 00000000 __vector_14
000000a2  w      .text 00000000 __vector_10
000000c8 g     F .text 00000090 __vector_16
00800100 g       .text 00000000 __data_start
000000a2  w      .text 00000000 __vector_18
000000a2  w      .text 00000000 __vector_20

Hex dump of sections deleted...

Disassembly of section .text:

00000000 <__vectors>:
   0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
   4: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
   8: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
   c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  10: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  14: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  18: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  1c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  20: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  24: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  28: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  2c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  30: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  34: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  38: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  3c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  40: 0c 94 64 00 jmp 0xc8 ; 0xc8 <__vector_16>
  44: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  48: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  4c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  50: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  54: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  58: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  5c: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  60: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
  64: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>

00000068 <__ctors_end>:
  68: 11 24       eor r1, r1
  6a: 1f be       out 0x3f, r1 ; 63
  6c: cf ef       ldi r28, 0xFF ; 255
  6e: d8 e0       ldi r29, 0x08 ; 8
  70: de bf       out 0x3e, r29 ; 62
  72: cd bf       out 0x3d, r28 ; 61

00000074 <__do_copy_data>:
  74: 11 e0       ldi r17, 0x01 ; 1
  76: a0 e0       ldi r26, 0x00 ; 0
  78: b1 e0       ldi r27, 0x01 ; 1
  7a: e2 ed       ldi r30, 0xD2 ; 210
  7c: f1 e0       ldi r31, 0x01 ; 1
  7e: 02 c0       rjmp .+4       ; 0x84 <.do_copy_data_start>

00000080 <.do_copy_data_loop>:
  80: 05 90       lpm r0, Z+
  82: 0d 92       st X+, r0

00000084 <.do_copy_data_start>:
  84: a0 30       cpi r26, 0x00 ; 0
  86: b1 07       cpc r27, r17
  88: d9 f7       brne .-10     ; 0x80 <.do_copy_data_loop>

0000008a <__do_clear_bss>:
  8a: 11 e0       ldi r17, 0x01 ; 1
  8c: a0 e0       ldi r26, 0x00 ; 0
  8e: b1 e0       ldi r27, 0x01 ; 1
  90: 01 c0       rjmp .+2       ; 0x94 <.do_clear_bss_start>

00000092 <.do_clear_bss_loop>:
  92: 1d 92       st X+, r1

00000094 <.do_clear_bss_start>:
  94: a9 30       cpi r26, 0x09 ; 9
  96: b1 07       cpc r27, r17
  98: e1 f7       brne .-8       ; 0x92 <.do_clear_bss_loop>
  9a: 0e 94 55 00 call 0xaa ; 0xaa <main>
  9e: 0c 94 e7 00 jmp 0x1ce ; 0x1ce <_exit>

000000a2 <__bad_interrupt>:
  a2: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>

000000a6 <setup>:
  a6: 08 95       ret

000000a8 <loop>:
  a8: 08 95       ret

000000aa <main>:
  aa: cf 93       push r28
  ac: df 93       push r29
  ae: 0e 94 ac 00 call 0x158 ; 0x158 <init>
  b2: 0e 94 53 00 call 0xa6 ; 0xa6 <setup>
  b6: c0 e0       ldi r28, 0x00 ; 0
  b8: d0 e0       ldi r29, 0x00 ; 0
  ba: 0e 94 54 00 call 0xa8 ; 0xa8 <loop>
  be: 20 97       sbiw r28, 0x00 ; 0
  c0: e1 f3       breq .-8       ; 0xba <main+0x10>
  c2: 0e 94 00 00 call 0 ; 0x0 <__vectors>
  c6: f9 cf       rjmp .-14     ; 0xba <main+0x10>

000000c8 <__vector_16>:
  c8: 1f 92       push r1
  ca: 0f 92       push r0
  cc: 0f b6       in r0, 0x3f ; 63
  ce: 0f 92       push r0
  d0: 11 24       eor r1, r1
  d2: 2f 93       push r18
  d4: 3f 93       push r19
  d6: 8f 93       push r24
  d8: 9f 93       push r25
  da: af 93       push r26
  dc: bf 93       push r27
  de: 80 91 04 01 lds r24, 0x0104
  e2: 90 91 05 01 lds r25, 0x0105
  e6: a0 91 06 01 lds r26, 0x0106
  ea: b0 91 07 01 lds r27, 0x0107
  ee: 30 91 08 01 lds r19, 0x0108
  f2: 01 96       adiw r24, 0x01 ; 1
  f4: a1 1d       adc r26, r1
  f6: b1 1d       adc r27, r1
  f8: 23 2f       mov r18, r19
  fa: 2d 5f       subi r18, 0xFD ; 253
  fc: 2d 37       cpi r18, 0x7D ; 125
  fe: 20 f0       brcs .+8       ; 0x108 <__vector_16+0x40>
 100: 2d 57       subi r18, 0x7D ; 125
 102: 01 96       adiw r24, 0x01 ; 1
 104: a1 1d       adc r26, r1
 106: b1 1d       adc r27, r1
 108: 20 93 08 01 sts 0x0108, r18
 10c: 80 93 04 01 sts 0x0104, r24
 110: 90 93 05 01 sts 0x0105, r25
 114: a0 93 06 01 sts 0x0106, r26
 118: b0 93 07 01 sts 0x0107, r27
 11c: 80 91 00 01 lds r24, 0x0100
 120: 90 91 01 01 lds r25, 0x0101
 124: a0 91 02 01 lds r26, 0x0102
 128: b0 91 03 01 lds r27, 0x0103
 12c: 01 96       adiw r24, 0x01 ; 1
 12e: a1 1d       adc r26, r1
 130: b1 1d       adc r27, r1
 132: 80 93 00 01 sts 0x0100, r24
 136: 90 93 01 01 sts 0x0101, r25
 13a: a0 93 02 01 sts 0x0102, r26
 13e: b0 93 03 01 sts 0x0103, r27
 142: bf 91       pop r27
 144: af 91       pop r26
 146: 9f 91       pop r25
 148: 8f 91       pop r24
 14a: 3f 91       pop r19
 14c: 2f 91       pop r18
 14e: 0f 90       pop r0
 150: 0f be       out 0x3f, r0 ; 63
 152: 0f 90       pop r0
 154: 1f 90       pop r1
 156: 18 95       reti

00000158 <init>:
 158: 78 94       sei
 15a: 84 b5       in r24, 0x24 ; 36
 15c: 82 60       ori r24, 0x02 ; 2
 15e: 84 bd       out 0x24, r24 ; 36
 160: 84 b5       in r24, 0x24 ; 36
 162: 81 60       ori r24, 0x01 ; 1
 164: 84 bd       out 0x24, r24 ; 36
 166: 85 b5       in r24, 0x25 ; 37
 168: 82 60       ori r24, 0x02 ; 2
 16a: 85 bd       out 0x25, r24 ; 37
 16c: 85 b5       in r24, 0x25 ; 37
 16e: 81 60       ori r24, 0x01 ; 1
 170: 85 bd       out 0x25, r24 ; 37
 172: ee e6       ldi r30, 0x6E ; 110
 174: f0 e0       ldi r31, 0x00 ; 0
 176: 80 81       ld r24, Z
 178: 81 60       ori r24, 0x01 ; 1
 17a: 80 83       st Z, r24
 17c: e1 e8       ldi r30, 0x81 ; 129
 17e: f0 e0       ldi r31, 0x00 ; 0
 180: 10 82       st Z, r1
 182: 80 81       ld r24, Z
 184: 82 60       ori r24, 0x02 ; 2
 186: 80 83       st Z, r24
 188: 80 81       ld r24, Z
 18a: 81 60       ori r24, 0x01 ; 1
 18c: 80 83       st Z, r24
 18e: e0 e8       ldi r30, 0x80 ; 128
 190: f0 e0       ldi r31, 0x00 ; 0
 192: 80 81       ld r24, Z
 194: 81 60       ori r24, 0x01 ; 1
 196: 80 83       st Z, r24
 198: e1 eb       ldi r30, 0xB1 ; 177
 19a: f0 e0       ldi r31, 0x00 ; 0
 19c: 80 81       ld r24, Z
 19e: 84 60       ori r24, 0x04 ; 4
 1a0: 80 83       st Z, r24
 1a2: e0 eb       ldi r30, 0xB0 ; 176
 1a4: f0 e0       ldi r31, 0x00 ; 0
 1a6: 80 81       ld r24, Z
 1a8: 81 60       ori r24, 0x01 ; 1
 1aa: 80 83       st Z, r24
 1ac: ea e7       ldi r30, 0x7A ; 122
 1ae: f0 e0       ldi r31, 0x00 ; 0
 1b0: 80 81       ld r24, Z
 1b2: 84 60       ori r24, 0x04 ; 4
 1b4: 80 83       st Z, r24
 1b6: 80 81       ld r24, Z
 1b8: 82 60       ori r24, 0x02 ; 2
 1ba: 80 83       st Z, r24
 1bc: 80 81       ld r24, Z
 1be: 81 60       ori r24, 0x01 ; 1
 1c0: 80 83       st Z, r24
 1c2: 80 81       ld r24, Z
 1c4: 80 68       ori r24, 0x80 ; 128
 1c6: 80 83       st Z, r24
 1c8: 10 92 c1 00 sts 0x00C1, r1
 1cc: 08 95       ret

000001ce <_exit>:
 1ce: f8 94       cli

000001d0 <__stop_program>:
 1d0: ff cf       rjmp .-2       ; 0x1d0 <__stop_program>

Disassembly of section .bss:

00800100 <__bss_start>:
  800100: 00 00       nop
...

00800104 <timer0_millis>:
  800104: 00 00       nop
...

00800108 <timer0_fract>:
...

Deleted very long disassembly of the debug support sections.

Crystal Filter

The post yesterday delivered my bag-o-rocks from China.  I ordered on eBay 100, 20 Mhz surface mount crystals for building crystal filters.



I am going to have to convince my buddy Eldon, WA0UWH to help me categorize these using his AIM-4170B analyzer before I solder them into a circuit.  $10 delivered from China is pretty good, but it remains to be seen how these crystals operate.

Tuesday, September 9, 2014

Updated PCA9546 library

As mentioned in a previous post, I have updated the PCA9546 library in order to fix the problem of one of the methods of the class being implemented as private when it needs to be public.  I also implemented a constructor that allows initialization of the class without selecting any channel as active.  Lastly, I implemented a change to the selectChannel method to allow deselecting all channels.

As promised, I am posting the entirety of the update here for your convenience.

File: PCA9546.h

#ifndef PCA9546_H
#define PCA9546_h

#ifndef P
#define PBUFSIZE (66)
extern char buf[PBUFSIZE];

#define  P(x) strcpy_P(buf, PSTR(x))
#endif

typedef enum 
{
  PCA9546_ERROR = 0,
  PCA9546_SUCCESS
} PCA9546_Status;

#define PCA9546_NOCHANNEL (0) // No channel selected
#define PCA9546_CHANNEL_1 (1) // Bit 1
#define PCA9546_CHANNEL_2 (2) // Bit 2
#define PCA9546_CHANNEL_3 (4) // Bit 3
#define PCA9546_CHANNEL_4 (8) // Bit 4

class PCA9546
{
public:
  PCA9546(uint8_t i2c_address);
  PCA9546(uint8_t i2c_address, uint8_t channel);
  bool selectChannel(uint8_t channel);
  
  PCA9546_Status status;
  uint8_t channel;

private:
  uint8_t i2c_address;
  uint8_t i2c_read();
  void i2c_write(uint8_t data);
};


#endif

File: PCA9546.cpp

/*
 * PCA9546 Library for Arduino
 *
 * MIT License
 *
 * Copyright Jeff Whitlatch - ko7m - 2014
 */

#include <Arduino.h>
#include <Wire.h>
#include "PCA9546.h"
#include "debug.h"


#define DEBUG(x ...)  // Default to NO debug    
//#define DEBUG(x ...) debugUnique(x)    // UnComment for Debug

// Initialize the PCA9546 and disable all channels
PCA9546::PCA9546(uint8_t PCA9546_address)
  i2c_address = PCA9546_address;

  Wire.begin();
  selectChannel(PCA9546_NOCHANNEL);
}

// Initialize the PCA9546 and enable the channel(s) indicated
PCA9546::PCA9546(uint8_t PCA9546_address, uint8_t channel)
  i2c_address = PCA9546_address;

  Wire.begin();
  selectChannel(channel);
}

// Send a channel selection word to the PCA9546
bool PCA9546::selectChannel(uint8_t channel)
{
  // Sanity check value passed.  Only least significant 4 bits valid
  if (channel <= 0xf)
  {
    i2c_write(channel);
    debug(P("Successfully selected PCA9546 channel"));
    status = PCA9546_SUCCESS;
  }
  else
  {
    debug(P("PCA9546 channel selection failed"));
    status = PCA9546_ERROR;
  }
  return (PCA9546_SUCCESS == status);
}

// Write a byte to I2C device.  There is only a single register.  If multiple bytes written, last one wins.
void PCA9546::i2c_write(uint8_t data)
{
  Wire.beginTransmission(i2c_address);
  Wire.write(data);
  Wire.endTransmission();
}

// Read the one byte register from the I2C device
uint8_t PCA9546::i2c_read()
{
  uint8_t rdata = 0xFF;
  Wire.beginTransmission(i2c_address);
  Wire.requestFrom(i2c_address, (uint8_t)1);
  if (Wire.available()) rdata = Wire.read();
  return rdata;
}

Minima Controller - 5V i2c LCD support

I have tested my Minima controller successfully for support of 16x2 or 20x4 LCD displays using a 5V i2c backpack.  Here is a 20x4 display running my changes to WA0UWH's latest firmware for Minima.



Only two wires plus power and ground required to support these displays.  The code is conditionally compiled with or without i2c display support as desired with the default to continue to support 6 wire displays.  Converting to an i2c display will be required in order to free up I/O pins to support such things as rotary controllers, keyer paddles, etc.  This change however will continue to work by default with the original Minima hardware configuration for the LCD display.  In order to enable the functionality, please uncomment the following line near the top of the file "radiono.ino".

    #define USE_I2C_LCD 1

  You will also need to obtain the i2c library LiquidTWI and install in your Arduino environment in order to build with this support.

It should be noted that my Minima Controller shield is not required to support i2c displays.

Please contact me directly by email at ko7m at arrl.net or by commenting on this post if you have any problems and I will do my best to assist.

Minima Controller - OLED Display

After some initial head scratching, I have my adaptation of the Minima sketch that supports my OLED display up and running on my controller shield.


I was initially not getting any joy out of the OLED display and it remained black despite my efforts.  However, I had stupidly placed the initialization of the PCA9546 multiplexer after the initialization of the OLED display.  Since the OLED cannot be addressed until the mux is set to channel 1, no joy.

I did write a somewhat useful utility that enumerates all i2c device id's that can be seen on the Arduino i2c bus as well as on each of the four channels of the mux which others may find useful.

Debugging this did point out to me an error in my PCA9546 code and a couple of changes I would like to make.

1. I have mistakenly made the selectChannel() method private, when it should be public.
2. The channel argument to selectChannel() is currently a bit field.  The bottom 4 bits indicate which of the four channels should be enabled.  I believe I will change this to just take a channel number rather than a bit field.  I also need a way to deselect all channels.
3. I need to provide a constructor for the PCA9546 class that will allow initialization without selecting any channel.

I will provide an updated listing of PCA9546 class with these changes in a separate post.

Here is the code for scanning for devices connected to the i2c bus and all channels of the PCA9546.

// i2c_scanner
//

#include <Wire.h>
#include "PCA9546.h"

PCA9546 *mux;
char buf[66];

void setup()
{
  Wire.begin();

  Serial.begin(115200);
  Serial.println("i2c Scanner");
  mux = NULL;
}

void printAddress(byte address)
{
  if (address<16) Serial.print("0");
  Serial.println(address, HEX);
}

void loop()
{
  uint8_t nChannel;
  
  for (nChannel = 0; nChannel <= 4; nChannel++)
  {
    Serial.print("Channel ");
    Serial.println(nChannel, DEC);
    byte error, address;
    int nDevices;
  
    Serial.println("Scanning...");
  
    nDevices = 0;
    if (nChannel > 0)
    {
      if (mux == NULL)
      {
        mux = new PCA9546(0x70, 1 << (nChannel - 1));
      }
      else
      {
        bool fRc = mux->selectChannel(1 << (nChannel - 1));
      }
    }
    
    for(address = 1; address < 127; address++ ) 
    {
      Wire.beginTransmission(address);
      error = Wire.endTransmission();
      
      // Just for good measure, try again
      if (error != 0)
      {
        delay(10);
        Wire.beginTransmission(address);
        error = Wire.endTransmission();
      }
  
      if (error == 0)
      {
        Serial.print("i2c device found at address 0x");
        printAddress(address);  
        nDevices++;
      }
      else if (error == 4) 
      {
        Serial.print("Unknown error at address 0x");
        printAddress(address);
      }    
    }
    if (nDevices == 0)
      Serial.println("No i2c devices found\n");
    else
      Serial.println("");
  }

  // Hang the script
  while (1==1);
}

Monday, September 8, 2014

Minima Controller Shield - Some measurements

Last evening I placed my second Si570 on my Minima Controller shield and am pleased to see it is functional as well.

The datasheet for the Si570 CMOS version says that supply current draw should typically be 90 mA with a maximum of 98 mA.  With both devices powered up, I am seeing a current draw on the 3V3 rail of 140 mA.

The Arduino UNO R3 board uses a TI LP2985-33DBVR regulator which according to its datasheet is rated for 150 mA.  There is nothing on the Arduino board that uses 3V3, but given the maximum current draw of two Si570 devices at 196 mA drove the decision to install a separate 3V3 800 mA regulator on the board.  At my current draw, I might be able to get away with using the Arduino 3V3 rail, but it is pushing things.  Another option might be to replace the regulator but again, I didn't want to modify the Arduino board.

Interestingly, the Arduino documentation says not to draw more than 50 mA from the 3V3 rail which makes me suspect that they may have trimmed the heat sink tab on the part.

I am seeing 13.5 dBm with no attenuation out of the Si570 devices so there is plenty of drive available to allow experimentation with different mixers.  The PI pad resistors can be populated to set any level desired.



I have Wayne, NB6M's Minima build at my disposal for a few weeks to be able to try out the new shield for both VFO and BFO signal sources and am looking forward to seeing how this works out in a practical sense.



Sunday, September 7, 2014

Minima Controller Shield programming

UPDATED:

As was intended, programming of the controller shield is trivial.  A single additional class was required and no changes to other code or libraries was necessary.  With the Si570 devices behind a multiplexer, the only change necessary to the main Minima sketch is to select the multiplexer channel containing the Si570 you wish to talk to.  For a controller with a single Si570, selecting that channel needs only happen at startup and existing code continues to work unchanged.

I created a driver class for the PCA9546 as follows:

File: PCA9546.h

#ifndef PCA9546_H
#define PCA9546_h

#ifndef P
#define PBUFSIZE (66)
extern char buf[PBUFSIZE];

#define  P(x) strcpy_P(buf, PSTR(x))
#endif

typedef enum 
{
  PCA9546_ERROR = 0,
  PCA9546_SUCCESS
} PCA9546_Status;

#define PCA9546_CHANNEL_1 (1) // Bit 1
#define PCA9546_CHANNEL_2 (2) // Bit 2
#define PCA9546_CHANNEL_3 (4) // Bit 3
#define PCA9546_CHANNEL_4 (8) // Bit 4

class PCA9546
{
public:
  PCA9546(uint8_t i2c_address, uint8_t channel);

  PCA9546_Status status;
  uint8_t channel;

private:
  uint8_t i2c_address;
  bool selectChannel(uint8_t channel);
  uint8_t i2c_read();
  void i2c_write(uint8_t data);
};

#endif

File: PCA9546.cpp

/*
 * PCA9546 Library for Arduino
 *
 * MIT License
 *
 * Copyright Jeff Whitlatch - ko7m - 2014
 */

#include <Arduino.h>
#include <Wire.h>
#include "PCA9546.h"
#include "debug.h"


#define DEBUG(x ...)  // Default to NO debug    
//#define DEBUG(x ...) debugUnique(x)    // UnComment for Debug

// Initialize the PCA9546 and enable the channel(s) indicated
PCA9546::PCA9546(uint8_t PCA9546_address, uint8_t channel)

  i2c_address = PCA9546_address;

  Wire.begin();
  selectChannel(channel);
}

// Send a channel selection word to the PCA9546
bool PCA9546::selectChannel(uint8_t channel)
{
  // Sanity check value passed.  Only least significant 4 bits valid
  if (channel <= 0xf)
  {
    i2c_write(channel);
    debug(P("Successfully selected PCA9546 channel"));
    status = PCA9546_SUCCESS;
  }
  else
  {
    debug(P("PCA9546 channel selection failed"));
    status = PCA9546_ERROR;
  }
  return (PCA9546_SUCCESS == status);
}

// Write a byte to I2C device.  There is only a single register.  If multiple bytes written, last one wins.
void PCA9546::i2c_write(uint8_t data)
{
  Wire.beginTransmission(i2c_address);
  Wire.write(data);
  Wire.endTransmission();
}

// Read the one byte register from the I2C device
uint8_t PCA9546::i2c_read()
{
  uint8_t rdata = 0xFF;
  Wire.beginTransmission(i2c_address);
  Wire.requestFrom(i2c_address, (uint8_t)1);
  if (Wire.available()) rdata = Wire.read();
  return rdata;
}

So, this new class has been added to the Minima source files.  In the Minima main sketch, I add the following code right before including "Si570.h":

#include "PCA9546.h"

I then add the definition of the PCA9546 I2C address right before the one for the Si570:

#define PCA9546_I2C_ADDRESS 0x70

I then create a global variable to hold the PCA9546 object right before the one defined to hold the Si570 object:

PCA9546 *mux;

Ok, so the only thing required now is to initialize the multiplexer and select channel 1.  The rest of the Minima sketch can run without change.  I put the following code right before the initialization of the Si570:

  // Initialize the PCA9546 multiplexer and select channel 1
  mux = new PCA9546(PCA9546_I2C_ADDRESS, PCA9546_CHANNEL_1);
  if (mux->status == PCA9546_ERROR)
  {
    printLine2CEL(P("PCA9546 init error"));
    delay(3000);

  }

I have posted the PCA9546 driver source and integration with Eldon Brown's (WA0UWH) latest Minima sketch on my public branch of my Minima repository on Github.  Please check it out at https://github.com/ko7m/radiono_ko7m.

If integration of the files above provides any grief for anyone cloning my shield, please contact me for assistance and I will be happy to help.  Direct email is ko7m at arrl.net.