Wednesday, January 22, 2014

Baite Pro Mini review

I recently purchased a Baite Pro Mini board on Aliexpress.
Three weeks after ordering, it arrived by registered mail, exactly as depicted.  This one is different from the other Pro Mini boards I've seen in a couple ways.  First is that it uses a QFN (quad flat no-lead) package ATmega328P instead of the slightly larger and more common QFP (quad flat pack).  The other difference is that it uses a crystal oscillator instead of a smaller resonator.  Crystal oscillators are more accurate, which is useful if you need accurate time keeping.  The board came with no documentation, so I assumed it would work like a standard Arduino Pro Mini.

After opening the package I just soldered on the 90 degree header for the serial connections, then connected it into a PL2303HX USB-TTL adapter.  The red power LED came on, and the blue LED started flashing - indicating the pre-loaded blink sketch was running.  The blue LED is quite bright, even though it uses a 750 Ohm current-limiting resistor.  I selected Pro Mini 5v @ 16Mhz from the Arduino IDE, pressed reset, and tried uploading a test sketch.  Avrdude timed out, so I tried again.  There was only one quick blink of the LED - indicating a short timeout for the bootloader.  In case the board was using optiboot @ 115,200kbps, I changed the upload speed in the boards.txt file, and tried a few more times but still no luck.

I decided to flash a new bootloader so I'd have a known configuration.  I chose the Uno from the boards menu because it's configuration uses the optiboot bootloader @ 115,200kbps.  I connected my USBasp, selected USBasp from the programmer menu, then selected "burn bootloader".
After 10-15 seconds it was finished.  I loaded my test sketch, clicked the upload button, pressed reset on the Pro Mini after the "Running AVRDUDE..." message appeared, and the "Done uploading" message quickly appeared.  My sketch was loaded and running.  Considering the $3.65 I paid for it, I think the Baite Pro Mini is a good buy.

2014/09/22 update:

The Pro Mini boards are now selling for $2.50 or less on Aliexpress, so I picked up another one.  This time I checked the fuse settings with avrdude:
avrdude.exe: safemode: Fuses OK (E:05, H:DA, L:FF)

The bootsz fuse is 01, which is 2Kbyte (1Kword), which confirms they do not come with optiboot flashed (which is only 512 bytes).

Saturday, January 11, 2014

AVR half-duplex software UART supporting single pin operation

Many of the ATtiny MCUs have no hardware UART limited number of pins.  Arduino tiny cores uses the TinyDebugSerial class which is output only, so using serial input requires extra code.  I have written a small software serial UART which can share a single pin for Rx & Tx when a simple external circuit is used.

To understand the circuit, a tutorial on TTL serial communication may help.  When the TTL serial adapter is not transmitting, the voltage from the Tx pin keeps Q1 turned on, and the AVR pin (Tx/Rx) will sense a high voltage, indicating idle state.  When the AVR transmits a 0, with Q1 on, Rx will get pulled low indicating a 0.  R1 ensures current flow through the base of Q1 is kept below 1mA.  When the AVR transmits a 1, Rx will no longer be pulled low, and Rx will return to high state.  When the serial adapter is transmitting a 0, D1 will allow it to pull the AVR pin low.  With no base current, Q1 will be turned off, and the Rx line on the serial adapter will be disconnected from the transmission.

I've written the serial code to work as an Arduino library.  Compiled it uses just 62 bytes of flash, and does not require any RAM (there is no buffering).  As it is written in AVR assembly, it will support very high baud rates - up to 460.8kbps at 16Mhz.  The default baud rate of 115.2kbps is defined in BasicSerial3.h, and can be changed with the BAUD_RATE define.  The library defaults to use PB5 for both Tx and Rx.  It can be changed with the UART_Tx and UART_Rx defines in BasicSerial3.S.  Here's an example sketch to that uses it:
#include <BasicSerial3.h>

// sketch to test Serial

void setup()
{
}

void serOut(const char* str)
{
   while (*str) TxByte (*str++);
}

void loop(){
  byte c;
  serOut("Serial echo test\n\r");
  while ( c = RxByte() ){
    TxByte(c);
  }
  delay(1000);
}

Here's a screen shot of putty running the example:

2020 Update

Over the past several years I've written many updated and improved bit-bang UARTs.  Instead of the BasicSerial code in this post, I recommend using picoUART.  It supports a wider range of baud rates, and the bit timing is accurate to the cycle.

Thursday, January 9, 2014

nrf24l01+ control with 3 ATtiny85 pins

Nrf24l01+ modules are a cheap and low-power option for MCU wireless communication.  Libraries are available for Arduino, and for arduino compatible MCUs like the ATTiny85.  Controlling the nrf modules usually requires power plus 5 pins - CE, CSN, SCK, MOSI, & MISO.  With pin-limited MCUs like the ATtiny85, 5 pins is a lot to tie up.  On something like the Digispark, with PB3 and PB4 hard-wired to USB+ and USB-, using the nrf24l01+ modules might seem impossible.  Another issue is that although the nrf inputs are 5v tolerant, Vcc must be between 1.9 and 3.6V.  I've designed a simple solution to provide 3V power as well as control the modules with just 3 of the pins on the ATtiny85.

For powering the nrf24l01+ module, a 3.3v regulator could be used, but a cheaper and simpler way is to drop a 5V supply to 3V using a 20mA-rated red led.  Most red LEDs have a forward voltage drop between 1.8 and 2.2V, leaving 3.2-2.8V for the nrf, which is well within the 1.9-3.6V requirement.

Controlling CE without using a pin on the AVR is also easy: just tie it high.  In my setup function, I set Mirf.cePin = 7 as a dummy since the tiny85 only has pins 0-6.  I later commented out the digitalWrite calls in Nrf24l::ceHi() and ceLow(), and removed the Mirf.cePin line from setup() which cut down on the size of my compiled sketch.

I initially thought the CSN line could be tied low, but when I tried it my test sketch was not getting a valid status back from the nrf module.  I also found section 8.3.1 of the datasheet: "Every new command must be started by a high to low transition on CSN."  So in order to control the nrf with just 3 pins, CSN needs to be multiplexed with one (or more) of SCK, MOSI, or MISO.  After a few different ideas, I came up with this circuit:
When SCK on the ATtiny85 goes high for several microseconds, C1 will charge through R1 and bring CSN high.  If SCK is brought low for several microseconds before being used to clock the SPI data, C1 will discharge through D1 and bring CSN low.  High pulses on SCK of less than a few microseconds during communication with the nrf won't last long enough to charge C1, so CSN will stay low.

To support the multiplexed SCK/CSN, I modified Mirf.cpp as follows:
void Nrf24l::csnHi(){
PORTB |= (1<<PINB2); // SCK->CSN HIGH
delayMicroseconds(64);  // allow csn to settle
}

void Nrf24l::csnLow(){
PORTB &= ~(1<<PINB2);  // SCK->CSN LOW
delayMicroseconds(8);  // allow csn to settle
}

The circuit still worked with a 32us delay in csnHi and 8us in csnLow, but I doubled those values to have a good safety margin.  The delays could be reduced with a smaller capacitor for C1.  Going lower than .01uF could risk CSN going high during the high clock pulses of SCK.

When connecting the nrf module to a tiny85, connect MISO(pin7) on the module to MOSI/DI(PB0), and not MISO/DO(PB1).  Here's the connections required:
nrf module  ATtiny85 pin
SCK(5)      PB2 (physical pin 7)
MOSI(6)     PB1 (physical pin 6)
MISO(7)     PB0 (physical pin 5)

I also changed TinyDebugSerial.h to define TINY_DEBUG_SERIAL_BIT 5, and connected pb5 to the Rx line of my ttl serial module.

Finally, here's my test sketch.  When it runs, it reports a status of 'E', which is the reset value of the status register according to the datasheet.  If you connect things wrong it will usually report 0 or FF.
#include <SPI85.h>
#include <Mirf.h>
#include <MirfHardwareSpi85Driver.h>

void setup()
{
  Serial.begin(115200);

  Mirf.spi = &MirfHardwareSpi85;
  Mirf.init();
}

void loop()
{
  uint8_t nrfStatus;
  delay(3000);
  Serial.print("\nMirf status: ");
  nrfStatus = Mirf.getStatus();
  // do back-to-back getStatus to test if CSN goes high
  nrfStatus = Mirf.getStatus();
  Serial.print(nrfStatus, HEX);
}

Update 2014/06/12:
I've noticed that power usage on the nRF modules shoots up if I leave any inputs (CSN, SCK, MOSI) floating.  With my circuit above CSN will never float, but SCK and MOSI should be pulled high or low when not in use, especially for battery-powered devices.

Update 2014/10/04:
I've written another post titled nRF24l01+ reloaded with more details on controlling these modules.

Update 2015/05/23:
I've figured out a way to control nRF modules with just 2 pins using custom bit-banged SPI routines.

Friday, December 27, 2013

Writing AVR assembler code with the Arduino IDE

Although I have written a lot of code in high-level languages like C++, I enjoy writing assember the most.  For inserting assembler code into Arduino sketches, you can read a gcc inline assembly guide.  If you have some assembly code and want to use it, there is an easier way than converting it to inline assembly; you can make it a library.

The Arduino Serial class consumes a lot of resources, and even the tiny cores serial class (TinyDebugSerial) adds overhead to the half duplex software UART code it seems to be based on.  I decided to integrate my implementation of AVR305 with an Arduino sketch.

I started by making a directory called BasicSerial in the libraries directory.  Inside I created a BasicSerial.S file for my assember code.  In order for assembler code to be callable from C++, it is necessary to follow the avr-gcc register layout and calling convention, and mark the function name global.  The TxByte function takes a single char as an argument, which gcc will put in r24.  The Arduino core uses interrupts which would interfere with the software UART timing, so interrupts are disabled at the start of TxByte and re-enabled at the end.  Here's the code:
#include <avr/io.h>
; correct for avr/io.h 0x20 port offset for io instructions
#define UART_Port (PORTB-0x20)
#define UART_Tx 0

#define bitcnt r18
#define delayArg 19

#if F_CPU == 8000000L
  #warning Using 8Mhz CPU timing 
  #define TXDELAY 21
#elif F_CPU == 16000000L
  #warning Using 16Mhz CPU timing 
  #define TXDELAY 44 
#else
  #error unrecognized F_CPU value
#endif

.global TxByte
; transmit byte in r24 - 15 instructions
; calling code must set Tx line to idle state (high) or 1st byte may be lost
; i.e. PORTB |= (1<<UART_Tx)
TxByte:
cli
        sbi UART_Port-1, UART_Tx              ; set Tx line to output
        ldi bitcnt, 10                              ; 1 start + 8 bit + 1 stop
        com r24                                    ; invert and set carry
TxLoop:
        ; 10 cycle loop + delay
        brcc tx1
        cbi UART_Port, UART_Tx                  ; transmit a 0
tx1:
        brcs TxDone
        sbi UART_Port, UART_Tx                  ; transmit a 1
TxDone:
        ldi delayArg, TXDELAY
TxDelay:
; delay (3 cycle * delayArg) -1
        dec delayArg
        brne TxDelay
        lsr r24
        dec bitcnt
        brne TxLoop
reti ; return and enable interrupts

The last thing to do is to create a header file called BasicSerial.h:
extern "C" {
void TxByte(char);
}
If the extern "C" is left out, C++ name mangling will cause a mismatch.  To use the code in the sketch, just include BasicSerial.h, and call TxByte as if it were a C function.  Here's a sample sketch:
#include <BasicSerial.h>

// sketch to test Serial

// change LEDPIN based on your schematic
#define LEDPIN  PINB1

void setup(){
  DDRB |= (1<<LEDPIN);    // set LED pin to output mode
}

void serOut(const char* str)
{
   while (*str) TxByte (*str++);
}

void loop(){
  serOut("Turning on LED\n");
  PORTB |= (1<<LEDPIN);  // turn on LED
  delay(500);            // 0.5 second delay
  PORTB &= ~(1<<LEDPIN); // turn off LED
  delay(1000);           // 1 second delay
}

Download and run the sketch, open the Serial Monitor at 115,200bps, an you should see this:

I've posted BasicSerial.zip containing BasicSerial.S and BasicSerial.h.  Have fun!

New year's update:

I've modified the code so the delay timing is calculated by a macro in BasicSerial.h.  Just modify the line:
#define BAUD_RATE 115200
BasicSerialv2.zip

Wednesday, December 4, 2013

Trimming the fat from avr-gcc code

Although writing in AVR assembly makes it easy to write programs that fit in a small codespace, writing in C and using AVR Libc is more convenient.  This article outlines how to write C code that avr-gcc will build to a minimal size.  There are a number of other guides for writing small AVR code including AVR 4027, but none of them seem to address the overhead of avr-gcc's start-up library (gcrt1).

Many people seem to be still using avr-gcc 4.3.3 as it usually generates smaller code than 4.5.3 and 4.7.  I recently tried avr-gcc 4.8.2 (linux RPM cross-avr-gcc-4.8.2-3.2) , and for the program I use here, it generates even smaller code than 4.3.3.

The test program uses the ATtiny85's internal temperature sensor and flashes the temperature using a LED.  When compiled using -Os it results in a 274-byte program:
avr-size temperature
   text    data     bss     dec     hex filename
    274       0       0     274     112 temperature.bu
With avr-gcc 4.8.2 that drops to 240 bytes:
 avr-size temperature-4.8
   text    data     bss     dec     hex filename
    240       0       0     240      f0 temperature-4.8

The difference is primarily in the startup files linked to the code.  Disassembling the code with avr-objdump -d shows the reset vector contains a jump to a function called __ctors_end:
   0:   0e c0           rjmp  .+28      ; 0x1e <__ctors_end>
0000001e <__ctors_end>:
  1e:   11 24           eor     r1, r1
  20:   1f be           out     0x3f, r1        ; 63
  22:   cf e5           ldi     r28, 0x5F       ; 95
  24:   d2 e0           ldi     r29, 0x02       ; 2
  26:   de bf           out     0x3e, r29       ; 62
  28:   cd bf           out     0x3d, r28       ; 61

The function __ctors_end falls into __do_copy_data, which falls into __do_clear_bss before an rcall to main followed by an rjmp to _exit.  In total it's about 50 bytes of code before calling main.  With avr-gcc 4.8.2, the only code before main is __ctors_end, or 16 bytes of what would seem to be overhead.

Before trying to cut out __ctors_end, I wanted to make sure the code in __ctors_end is really overhead that can be safely removed.  The first two lines clear SREG.  Section 8.1 of the ATtinyX5 datasheet states, "During reset, all I/O Registers are set to their initial values, and the program starts execution from the Reset Vector."  The datasheet also indicates it's initial value is 0, so the first two lines can go.  The last 4 lines set the stack pointer (SPL and SPH) to RAMEND, which section 4.6 of the datasheet indicates is their initial value.  So it is safe to get rid of __ctors_end and jump straight to main from the reset vector, for a savings of 16 bytes.

Another 30 bytes of data is used for the interrupt vector table (and even more than 30 bytes on the ATmega series MCUs).  Section 9.1 of the datasheet states, "If the program never enables an interrupt source, the Interrupt Vectors are not used, and regular program code can be placed at these locations."  My temperature blinking program doesn't use interrupts so more space can be saved by getting rid of the interrupt table.

The way to tell avr-gcc not to link in the startup code is -nostartfiles.  If that is all you do with your C code, then avr-gcc will stick the first object file at address 0 (the reset vector).  To ensure the reset vector contains a jump to main I wrote a small assembly program (crt1.S).  I this custom startup code instead of gcrt1 included with the compiler libraries.  The code isn't long, so I'll include it inline:
.org 0x0000
__vectors:
rjmp main

Compile it (avr-gcc -c crt1.S), and link it with your C code.  For compiling temperature.c here's the command line I used, including a couple of extra flags helpful for generating small code:
avr-gcc -mmcu=attiny85 -Os -fno-inline-small-functions -mrelax -nostartfiles crt1.o    temperature.c   -o temperature

The resulting program is 190 bytes, saving 84 bytes vs. avr-gcc 4.3.3 or 50 bytes vs. 4.8.2:
avr-size temperature
   text    data     bss     dec     hex filename
    190       0       0     190      be temperature

Note that many virtual bootloaders for the ATtiny MCUs will cause problems with this technique as they tend to assume application code doesn't start until after the interrupt vector table.  MCUs with hardware bootloader support (i.e. the ATmega series) will not have problems.  Picoboot, the bootloader I am writing, will only assume the reset vector contains an rjmp to the start of application code and therefore will work with my custom crt1.o.

Monday, November 25, 2013

Testing IPv6 on Windows

I recently decided to test IPv6 on Windows 7.  It has Teredo support, which will use tunnelbroker.net's free service and assign an ipv6 host address to the PC.  I found a few sites with instructions on how to configure teredo including this one.  I was able to get it to work a bit, and tested it with test-ipv6.com. However I wasn't able to get it to work consistently, and I could never get windows to prefer teredo ipv6 connections over ipv4.  I don't know if the inconsistency was an issue with teredo or with the tunnel servers, so I decided to try something else.

While searching for information on ipv6 setup on Windows, I read about the Freenet6 Tunnelbroker.  I downloaded and installed the client software.  I clicked "connect" on the gogoClient utility, and in a couple seconds had a working tunnel:

It also prioritized ipv6 over ipv4, so when I went to sites like lg.he.net, the connection used ipv6.  The last thing I wanted to try was to try ipv6 only.  I couldn't completely disable ipv4, since it is needed for the tunnel.  So what I did was add a static route to the tunnel endpoint:
route add 81.171.72.12/32 192.168.1.1
and then I deleted the ipv4 default route:
route delete 0.0.0.0
I made sure I couldn't reach ipv4 sites by trying to ping 8.8.8.8 (which failed as expected).  Google sites including youtube were ipv6 reachable.  Microsoft sites like bing.com were not (no DNS AAAA records).  Facebook was ipv6 reachable, as was the main yahoo.com site, but not yahoo mail.  Unfortunately many sites I frequent such as hackaday.com and digitimes.com have no ipv6 connectivity, so I won't be going ipv6 only any time soon.

Wednesday, September 18, 2013

clone PL-2303HX USB to TTL serial adapters

USB-TTL modules using the PL-2303HX chip are the cheapest ones around ($1.40 from Fasttech).  The chip has a built-in 3.3v regulator, so the modules can supply both 5v and 3.3v power.  Drivers for them are included in the Linux kernel and in Mac OSX too.

Apparently the chip has been cloned, and as a result Prolific updated their Windows drivers to detect the clone chips and fail.  If you are getting "Error Code 10", the module will work with earlier versions of the driver.

For Windows 7 64-bit, this version works.  Unzip the file, then in device manager select device properties, update driver, have disk.  For 32-bit windows, this version works fine.

Besides its intended use, I have used it as a breadboard 3.3 & 5v power supply with a USB extension cable to a 5v USB power supply (or a computer USB port).

I also find the adapters handy as a simple voltage tester.  The T and R LEDs light up when pulled to ground, so I'll attach them to MCU pins to check for pulses.