Tuesday 7 July 2020

Flashed Before My Eyes: Nuvoton N76E003 CPU Part 1 of 2


Nuvoton N76E003AT20 Core Controller Board

Nowadays the CPUs I use most often are Raspberry Pi, ESP8266 and Arduino. And a couple of ancient Intel-based laptops and of course my ARM-based smartphone. I have desktop PCs which I have not turned on in years.

Software is key:  just because it is more convenient (and just as cheap) to solve a problem using Raspbian or Arduino IDE, I still have stocks of 6502, 8752AH, PIC16F84 and PIC18F14K50 which I have not used in decades. So why bother setting up yet another CPU development system?

At RM6.2 (USD1.50) the Nuvoton N76E003AT20 Core Controller Board compares unfavorably with the Arduino Nano 'compatible' or even the ESP-01S. Just because the last two is supported by the Arduino IDE and it vast library. The N76E003 is usually programmed in C, usually via a proprietary IDE like Keil or IAR.

But it has one thing going for it: it is used in my favorite ESP-01S Relay Board, LC Technology's 5V WiFi Relay.

LC Technology WiFi dual Relay Board

Often pirated and sold as "2-Channel WiFi Relay" at RM19(USD4.50) including the ESP-01S CPU, it has been a mainstay of many IoT projects. Unlike some even cheaper WiFi relay boards, this one uses a serial TTL interface and has its own CPU, the Nuvoton N76E003.

There are two push-buttons for input and 3 LEDs, which can be PWM-ed. The onboard AMS1117-3.3 LDO gives it a good input power range, from 15V to 5V. And it still has spare, usable CPU pins. Together with its relays and WiFi capability, this makes for a very capable IoT design. Here's my hack (based on Zaschipas) on the 1 relay version of this board.

Indeed the same basic design is used in very many cheap China IoT devices (smart light bulbs, power switches, etc), usually supported by the eWeLink App. The IoT and WiFi portion has already been open-sourced via Tasmota and others, but if you can re-program the N76E003, you can extend or re-purpose it. And even better if you can use open-source programmers and IDE.

Since this is probably a low-use system I aimed for a cheap CPU programmer, something sold as "Nu-Link Simulator Offline Download Function Full Series of N76E003" for RM58. 

Nu-Link Simulator Offline Download Function Full Series of N76E003


This is probably derived from Nuvoton's Nu-Link-Me programmer so the schematics are not likely to be very different.

Nuvoton Nu-Link-Me

The usual software for Nuvoton Nu-Link-Me is the proprietary Keil or IAR. There is a delicious  NuEclipse for Linux but sadly it is for their ARM-based NuMicro series. You can probably run Keil with Wine in Line, but I decided to try erincandescent's nuvoprog.

nuvoprog runs on Go. I used a Debian system as nuvoprog had problems installing on my Slackware machine.  There's the usual Debian preamble:

# apt-get update
# apt-get upgrade

Next I downloaded Go, and installed it:
# tar -C /usr/local -xzf go1.14.4.linux-amd64.tar.gz

# vi /etc/profile

Added /usr/local/go/bin to PATH

# export PATH=$PATH:/usr/local/go/bin

A quick test is in order:

# cat hello.go
package main
import "fmt"
func main() {
  fmt.Printf("hello, world\n")
}

# go build hello.go
# ./hello
hello, world

Now for nuvoprog:
# go get -u github.com/erincandescent/nuvoprog
go: missing Git command. See https://golang.org/s/gogetcmd
package github.com/erincandescent/nuvoprog: exec: "git": executable file not found in $PATH

OK, I need git.

# apt-get install git
root@aspire5050:/home/heong/go# go get -u github.com/erincandescent/nuvoprog
# github.com/karalabe/hid
exec: "gcc": executable file not found in $PATH

And gcc.

#  apt install build-essential

# gcc --version
gcc (Debian 8.3.0-6) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

# apt-get install manpages-dev

Now it works:
# go get -u github.com/erincandescent/nuvoprog

Rather inconveniently it was installed in /root:
# ls -l /root/go/bin
total 5184
-rwxr-xr-x 1 root root 5306096 Jul  6 17:22 nuvoprog

I plugged in the Nu-Link programmer and got:
#/root/go/bin/nuvoprog read -t n76e003 dev.ihx
Error: Unsupported device
Usage:
  nuvoprog read [outfile.ihx] [flags]

Flags:
  -h, --help   help for read

Global Flags:
  -t, --target string   target device
  -v, --verbose         make verbose (enable debug logging)

Unsupported device

At least it recognized the programmer. Time to plug in the N76E003 Core Controller board. A little hiccup here: the Nu-Link programmer (and cable) is dual row by 5 way and the Core Controller board is a single row 8-pin header.

Note the programming port on the left and unpopulated J2


A quick check of the pinouts showed that the Nu-Link only used one of its 2 rows. The Core Controller programming port pinouts show that the first 5 pins are compatible with the Nu-Link pinout. So if I soldered a 5-way header into the Core Controller this would fit the Nu-Link cable.

If you orientate the cable connector via its polarizing tab when plugged in correctly overhangs the edge Core Controller.

Check that your Nu-Link voltage is set to 3V3, and just like that, it works:
#/root/go/bin/nuvoprog read -t n76e003 dev.ihx
# ls -l
total 122896
drwxr-xr-x 2 root  root       4096 Jul  6 17:29 bin
-rw-r--r-- 1 root  root      29256 Jul  6 17:48 dev.ihx
-rw-r--r-- 1 heong heong 123711003 Jul  6 17:05 go1.14.4.linux-amd64.tar.gz
-rwxr-xr-x 1 root  root    2080884 Jul  6 17:10 hello
-rw-r--r-- 1 root  root         74 Jul  6 17:10 hello.go
drwxr-xr-x 3 root  root       4096 Jul  6 17:29 src

The resulting file dev.ihx is blank, but then so is my Core Controller board.
# cat dev.ihx | head
:020000040003F7
:08000000FFFFFFFFFFFFFFFF00
:020000040000FA
:20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00
:20002000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0
:20004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
:20006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
:20008000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80
:2000A000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF60
:2000C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF40

For a compiler, I used the tried and true sdcc:

# apt-get install sdcc

Next we want a simple 'Hello, world" toy program for the N76E003 Core Controller. I did not need to look far, for erincandescent has blink_raw.c. Unlike many other sample programs, it only needed one include file, n76e003.h. I downloaded both files to the same directory and simply modified the path to the include file. Also note corrected output pin P15:

# cat blink.c
/* Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
#include <stdint.h>
#include "n76e003.h"
// 16Mhz clock
#define CLOCK 16000000L
// Divide by 12
#define T0CLOCK ((CLOCK)/12L)
// Per milisecond
#define T0_1MS ((T0CLOCK)/1000L)
static void msdelay(uint32_t count)
{
        uint16_t reload = -T0_1MS;
        // Input = Fsys/12
        SET_FIELD(CKCON, T0M, 0);
        // Mode 1
        SET_FIELD(TMOD,  T0M, 1);
        // Start
        TR0 = 1;
    while (count != 0)
    {
        TL0 = reload      & 0xFF;
        TH0 = reload >> 8 & 0xFF;;
        while(!TF0);
        TF0 = 0;
            count--;
    }
    TR0 = 0;
}
void main() {
        // Set pins in old-skool Quasi Bidirectional mode
        P0M1 = 0;
        P0M2 = 0;
        P1M1 = 0;
        P1M2 = 0;
        P3M1 = 0;
        P3M2 = 0;
        for (;;) {
                int i;
                for (i = 0; i < 10; i++) {
                        P15 = 0;
                        msdelay(1000);
                        P15 = 1;
                        msdelay(1000);
                }
                msdelay(2000);
        }
}
You should also download the config.json file:
# cat ./config.json
{
    "boot_select": "aprom",
    "pwm_enabled_during_ocd": false,
    "ocd_enabled": true,
    "reset_pin_disabled": false,
    "locked": false,
    "ldrom_size": "0kb",
    "bod_disabled": false,
    "bod_voltage": "2v2",
    "iap_enabled_in_brownout": false,
    "bod_reset_disabled": false,
    "wdt": "disabled"
}

It compiles nicely (ignore the warning):

# sdcc blink.c -D FOSC_160000
blink.c:24: warning 158: overflow in implicit constant conversion

# ls -l
total 224
-rw-r--r-- 1 root root 13593 Jul  6 18:40 blink.asm
-rw-r--r-- 1 root root  1428 Jul  6 18:31 blink.c
-rw-r--r-- 1 root root   664 Jul  6 18:40 blink.ihx
-rw-r--r-- 1 root root   242 Jul  6 18:40 blink.lk
-rw-r--r-- 1 root root 42153 Jul  6 18:40 blink.lst
-rw-r--r-- 1 root root 26106 Jul  6 18:40 blink.map
-rw-r--r-- 1 root root  1163 Jul  6 18:40 blink.mem
-rw-r--r-- 1 root root  6756 Jul  6 18:40 blink.rel
-rw-r--r-- 1 root root 42153 Jul  6 18:40 blink.rst
-rw-r--r-- 1 root root 49393 Jul  6 18:40 blink.sym
drwxr-xr-x 2 root root  4096 Jul  6 18:30 include
-rw-r--r-- 1 root root  8337 Jul  6 18:40 n76e003.h

And programs just as nicely with:

# /root/go/bin/nuvoprog program -t n76e003 -c @./config.json -a ./blink.ihx

But there is a problem: the LED did not blink. Luckily mark-fink mentioned that a Core Controller board just like mine had an unpopulated jumper J2 that needed soldering. And, the pin controlling the LED is P15 instead of P12.

Add a jumper (in green) to the Core Controller board for blink_raw.c to work


After a recompile and reprogram the Core Controller blinked nicely. OK so I have a working N76E003AT20 development system. 

Now for the LC Technology Dual Relay WiFi board. The header pinouts were almost identical, except CLK and DAT were reversed. Naughty of them, but never mind: it is just an inconvenience. Another cable should do the trick.
            
            Nu-Link                  2-channel relay
            3V3
            DAT                            CLK
            CLK                            DAT
            RST                            RST
            GND                            GND

Nu-Link programmer wired to Relay Board. Remember to remove the ESP-01S from its socket while programming


The relay board is a 5V device. If you popped the Nu-Link case open, there is a jumper to set the Nu-Link voltage to 5V. Be careful to set it back to 3V3 when using it for the Core Controller though, which takes 3V3 at the programming port.

If we now try to read the N76E003 progran from the relay board it fails:

# /root/go/bin/nuvoprog read -v -t n76e003  ../nuvoton/2channel_relay_readback.ihx
2020/07/06 20:25:22 >  013efffffffffffffffffffffffffffffffffffffffffffffffffffff
fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
2020/07/06 20:25:22 <  0114fd1a00000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 Setting config {1000 N76E003 3300 0 0}
2020/07/06 20:25:22 >  0218a2000000e803000000080000e40c0000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  020ca20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Performing reset {Auto ICP Mode Ext Mode}
2020/07/06 20:25:22 >  0310e2000000000000000500000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0304e20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Performing reset {None (NuLink) ICP Mode Ext Mode}
2020/07/06 20:25:22 >  0410e2000000050000000500000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0404e20000000920010068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK
2020/07/06 20:25:22 Checking device ID
2020/07/06 20:25:22 >  0508a3000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
2020/07/06 20:25:22 <  0508a30000005036ff0068882f615a01320100002000000064c2a6e91
28d78a54955484b46e6291d69bc03b6c70319896b2b8892674ef446d10e15c0d12a907c
2020/07/06 20:25:22 OK, Device ID  0x00ff3650
Error: Unsupported device
Usage:
  nuvoprog read [outfile.ihx] [flags]

Flags:
  -h, --help   help for read

Now it does look like the N76E003 is responding, except now the CPU Device ID 0x00ff3650 is not recognized. This will be addressed in Part 2.

So, we have a Linux development system for the N76E003 and showed it to work for the N76E003 Core Controller board, but not for the LC Technology Relay board. 

Happy Trails

No comments:

Post a Comment