I'm skim-reading this on a phone so following the nuances of the discussion is tricky but I am working on a range of intelligent peripherals that are based on a single model of a sub £1 chip. The inter - chip comms is going to be I2C....
'sub £1' is a rather higher price point than being discussed here, which is 74xx/4000 series logic replacement.
The N76E003 has a 400kHz i2c slave hardware block, so i2c could easily be added as a supported link.
(The State Logic looks to have 5 states for Slave receiver, and 6 states for slave transmitter mode, so code will be small)
i2c needs 2 wires, but has an advantage you can clip-on many peripherals to the same bus.
I'm skim-reading this on a phone so following the nuances of the discussion is tricky but I am working on a range of intelligent peripherals that are based on a single model of a sub £1 chip. The inter - chip comms is going to be I2C....
'sub £1' is a rather higher price point than being discussed here, which is 74xx/4000 series logic replacement.
The N76E003 has a 400kHz i2c slave hardware block, so i2c could easily be added as a supported link.
(The State Logic looks to have 5 states for Slave receiver, and 6 states for slave transmitter mode, so code will be small)
i2c needs 2 wires, but has an advantage you can clip-on many peripherals to the same bus.
I'd have to agree. The current focus is on a few specific controllers, considered for specific price and capabilities. The target language is C to begin with, but all languages will be considered eventually.
That said I hope there will be overlap in both this project and yours and that we can help each other.
Are you using i2c to allow for multiple devices on the same bus?
@jmg can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
... can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
Yes, that's the basis of RS485 networking (which uses a twisted wire pair). For more localized signaling, you can just use open-collector drivers on one wire.
... can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
Yes, that's the basis of RS485 networking (which uses a twisted wire pair). For more localized signaling, you can just use open-collector drivers on one wire.
-Phil
That is what I thought I had read. Thanks for the info
...
@jmg can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
hmm, yes, certainly, if you wanted.
The UART has a choice of 8b and 9b modes, (9b is a little less PC friendly, as PC-UARTS cannot quickly wiggle the mark/space parity, unless you buy the EXAR ones that supports 9b native )
You can easily allocate those 8-9 bits as any address/command mix.
Perhaps something like
RB8=1 :: Address_7 bits + 1b Echo
RB8=0 :: Data payload for Peripheral configuration
Sent as E.aaaaaaa.RB8
eg
1.aaaaaaa.1 - Immediate Echo selected 1 of 128 peripheral from slave aaaaaaa
0.aaaaaaa.1 - Address Slave aaaaaaa, multi byte command, no echo - follows with cccccccc.0, ddddddd.0, etc
You do need to set the address value somewhere - it can be compile time, or spare pins, or ADC to 2 resistors, or a mix of these.
If you want to work with any PC-UART, then you can drop to 8b, and (eg) have a 6 bit address field, or maybe 7b can still be squeezed in via a prefix approach
1.aaaaaaa - Immediate echo of 1 of 127 peripheral data byte(s)
1.0000000, 1.aaaaaaa - Command prefix tells all slaves do not echo following next address - command/config info will follow.
Send any number of 0.ddddddd 7 bit payload config bytes, until terminate with Address token (1.aaaaaaa)
-or-
For MCU-networks, we have also used a UART-RING topology. This uses 2 pins on every node, but now address management is simply by physical loop location.
I call this a twisted-ring, using the 9th bit as an frame edge-flag, and a simple common rule is N bytes after 9b _/= are extracted/replaced, with 9th bit cleared. All other bytes are simply passed along.
Result is first slave removes 4 bytes, and echos 4 byte reply, with 9th bit low, moving the frame edge for NEXT slave along 4 bytes.
Slaves can be counted/discovered by checking delay effect, and you have a natural integrity check.
eg Knowing < 50 nodes, 4B each, send 200 bytes 9b=0, then start sending 9b=1. 9 slaves will return 36 bytes echos with 9b=0, and then 37th byte+ has 9b=1
If you make the 9th bit normally high for data then it looks like a stop bit and fully compatible with PC UARTs etc. PCs though can still change the 9th bit, except to the PC it's called a parity bit, so it is possible for the PC to address the one-wire network if it really had to.
However most micros expect the 9th bit to be high when it sees an address and can interrupt the CPU or automatically match and enable it. The state of the 9th bit was an unfortunate design decision at the time but one we need to work in with.
I don't bother using slow open-collector though as all we are trying to do is to protect I/O pins, not arbitrate, in which case it is easier to add a series resistor, and 100R is sufficiently high enough for that purpose. Some of my early 9th bit network designs were in fire control panels which were quite large so using only single wire was quite useful for this purpose. For anything outside of that and up through the many levels I used RS485. I even had RS485 working in an early graphic elevator display over untwisted fire-proof cable that snaked up and down through the levels and from lift well to lift well, low rise section to high rise section, and to make it work on the one continuous cable I just simply reduced the speed to 19.2k.
But 9th bit mode doesn't have to be all individual addresses. I use some for global and group addresses where I don't expect a response but they should all listen, and some other codes for network control,
I2C works well when it it is all very close, it was after all designed for "Inter Integrated Circuit" (IIC which became I2C) operation whereby Philips TV at the time could have subsystems such as the tuner for instance distributed in the same chassis. Nonetheless I have been guilty of running this bus many scores of meters in the distant past
Is there a use for a UART modes on this project, given that it's known what the chips will be on both ends of the wire?
To clarify, several modes of UART, as well as I2C, USB, PWM, and PIC will need to be supported.
Would this be a better tools with a wide net cast over everything or could it benefit from prioritizing the common or useful modes over any esoteric options?
Is there a use for a UART modes on this project, given that it's known what the chips will be on both ends of the wire?
To clarify, several modes of UART, as well as I2C, USB, PWM, and PIC will need to be supported.
Would this be a better tools with a wide net cast over everything or could it benefit from prioritizing the common or useful modes over any esoteric options?
Not sure what you meant by 'UART modes', but the big variables around UART use are
a) BAUD rate
b) Bits Sent
and up a layer
c) Bytes in transaction
BAUD rate is quite easy to change, over a very wide range in the N76E003. ie 1MBd, to way down to something no PC can send, but an eyeball might decode
That's usually a single define, with some range tests if needed for init code.
Bits Sent is somewhat more structural - 9 bit modes have benefits, especially MCU-MCU, but will not work at top baud speeds from a PC, without a special device selection (EXAR)
PCs can work well enough to develop and test 9 bit code, using force-parity, as Peter mentions.
i2c slave is not too large a jump, and N76E003 has i2c Slave HW to 400k Baud.
USB is quite different, and bumps you into the SiLabs part, but there the 40k code size does open many doors.
Single-bridge parts usually need ~ 8k of code for a good set of features, so a 40k part could support a number.
eg UART-Bridge and i2c or SPI Bridge, that appears as 2 COM ports may be possible.
Not sure what your PWM meant, in peripheral comms-link context, but it would be possible to have a command group to set up PWM Frequency and duty, for LED or Buzzer additions to a keyboard code.
It would be possible to share pins for LED Drive and keyboard scan, with minor caveats.
N76E003 has quite comprehensive PWM, so could drive up to 6 Servos, or 6 LEDs with up to 16b setpoints. 10b~8b would give 16kHz~64kHz for DAC use.
Piezo element drive would also be well supported.
... can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
Yes, that's the basis of RS485 networking (which uses a twisted wire pair). For more localized signaling, you can just use open-collector drivers on one wire.
Some years ago I build a small home automation system with PIC16F628 nodes networked using a MAX485 in half duplex. SNAP from http://www.hth.com/snap/ was the protocol used to comunicate between the nodes and a "master" PC. The system is still working, unfortunately I don't know where the sources are buried.
When I heard SNAP I immediately thought of Subnetwork Access Protocol which also came out in 1998.
What advantages did your automation system gain by using SNAP over other protocols?
I see there are a number of error detection methodology, and the line "Requires minimal MCU resources to implement" gets my attention.
I don't think there is a hardware implementation of this. My reading says TI supports the powerline modem concept with an RS-485 network.
Do you think this is something worth implementing in software?
With 8 address bits you can easily address loads of devices on RS-485. What would be the potential gain?
I think there may be a couple nice features that could be extracted in concept for this project which would likely be the same as the answer(s) to my first question.
The PIC is out of scope for this project but if you benefited from any specific feature(s), that could be useful here.
You don't need a ninth bit for addressable networking. Just use escape sequences to set up the addressing and other coommands. For example, let $10 [DLE] be the escape character. A [DLE] sent by the master device on the network, followed by another character signals the slave devices downstream that the other character is a command or control character. That command could be "A" followed by an address. The slave whose address matches would then "wake up" and communicate with the master device.
What advantages did your automation system gain by using SNAP over other protocols?
I see there are a number of error detection methodology, and the line "Requires minimal MCU resources to implement" gets my attention.
Of course, 'minimal' is relative, and often claimed....
SNAP seems to allocate a SYNC byte value, but takes no efforts to ensure DATA/CRC or command bytes are NOT equal to that value.
I guess you could add a link monostable for additional sync protection, but they do not mention that ?
It also has quite high overhead - a single byte payload example, is 9 bytes out, and 8 bytes for ACK/NAK - a bit much for IO/keypad-like peripherals, but OK for wider networks like Home Automation ?
It also has quite high overhead - a single byte payload example, is 9 bytes out, and 8 bytes for ACK/NAK - a bit much for IO/keypad-like peripherals, but OK for wider networks like Home Automation ?
I agree, I think the most to extract from that would be a low-resource feature or two which could be implemented as options in this software. @macca has experience enough to help us identify those features, if any.
I'm going to take a closer look at the error detection. With either target MCU I think minimal error detection beyond the parity bit may be a necessary option for some high precision applications. It may be as simple as triple sending the data but there are better ways. I'm thinking XOR logic may be used in this option.
#include "simpletools.h" // So you can use the propeller to program the subMCU
#include "thincan.h" // This Project
enum ic ic_type = N76E003; // Specify subMCU by name
enum port protocol = UART0; // Reserves Port0>>Pins6&7 for UART with Propeller
long baud = 115200; // Baud rate
int bits_sent = 8; // Length of single byte
int bytes_trans = 6; // Bytes per transaction
int parity = 0; // Set parity bit to even
char buffer[6]; // Output buffer
char pins_scan_out[9] = "1111110010000000"; //Define 2D matrix Scan out pins, P0 then P1
char pins_scan_in[9] = "0000000001111111"; //Define 2D matrix Scan in pins, P0 then P1
int main_mcu() { // Main function of the subMCU
while(1) {
if(scan_2d(&buffer) != 0) { //On keypress event from scan
foo();
//TX here
}
}
}
int foo() { //All functions part of main function tree will be passed on to subMCU
int bar = 0;
return bar;
}
int main() { //Main function run by the programmer, run once, not included
print(program_ic()); //Examine globals, use once to configure subMCU
}
I think it could be this simple to program a 2D grid with UART communications.
You just compile this and run it on your propeller with the subMCU connected on a breadboard or something to program the subMCU. Your results and the subMCU status will print to your console.
I'm sure I've missed stuff, but what do you all think?
So getting back to the the title of your post, what you are kinda proposing as new has always been the standard of the industry that is implemented in a variety of non-standard ways. We don't called these sub-CPUs because we consider them sub-systems which may or may not share a common bus. PC keyboards have done this forever as they have their own micro scanning the keys and communicating bidirectionally with PC, the original method was the easy and simple P2/S clock and data.
So getting back to the the title of your post, what you are kinda proposing as new has always been the standard of the industry that is implemented in a variety of non-standard ways. We don't called these sub-CPUs because we consider them sub-systems which may or may not share a common bus. PC keyboards have done this forever as they have their own micro scanning the keys and communicating bidirectionally with PC, the original method was the easy and simple P2/S clock and data.
Forgive the keyboard concept. It's just an example. The point is more about the code structure and the benefit of standardizing a few MCUs so propeller users can focus on propeller code. I'll get some use out of it as well.
Not really proposing it as new. I cited an example and asked if a similar concept existed for propeller.
Other facilities can be included for all the servos and sensors that propeller users already use allowing them to free up pins, cogs, or both with a minimal effort.
I'm sure I've missed stuff, but what do you all think?
What you have said in your code example is that the main() function will run on the Propeller and that the functions main_mcu() and foo() will run on the attached MCU.
That is an interesting concept.
The missing part is: How on Earth are you going to make that happen?
That would require some super compiler/build system that can:
1) Analyse your source code, somehow recognize the parts that should run on the Propeller and those parts intended run on the attached MCU.
2) Use different compilers to build binary code form those extracted parts that targets the Propeller and attached MCU.
3) Probably have more than just two compilers available as the MCU could be of any architecture.
4) Down load the compiled binaries to the various processors and get them running.
That is a great idea. I have never seen a system that can do that. It sounds like a lot of work.
Typically people build systems out of different architecture processors. They have separate source code files for each device. Perhaps in different projects/sub projects. They have customized build systems to invoke the appropriate compilers and other tools for each part.
All those global variables have to go.
A common way to get the same source file to compile and build for different machines is to make use of #ifdef. The #ifdef and #if can be used to select which parts of the code are to be used in different places. That works when there are a few little things that need tweaking from platform platform. I can get very messy and make for unreadable code if the is a lot of it going on.
The missing part is: How on Earth are you going to make that happen?
That would require some super compiler/build system that can:
1) Analyse your source code, somehow recognize the parts that should run on the Propeller and those parts intended run on the attached MCU.
2) Use different compilers to build binary code form those extracted parts that targets the Propeller and attached MCU.
3) Probably have more than just two compilers available as the MCU could be of any architecture.
4) Down load the compiled binaries to the various processors and get them running.
That is a great idea. I have never seen a system that can do that. It sounds like a lot of work.
Typically people build systems out of different architecture processors. They have separate source code files for each device. Perhaps in different projects/sub projects. They have customized build systems to invoke the appropriate compilers and other tools for each part.
All those global variables have to go.
A common way to get the same source file to compile and build for different machines is to make use of #ifdef. The #ifdef and #if can be used to select which parts of the code are to be used in different places. That works when there are a few little things that need tweaking from platform platform. I can get very messy and make for unreadable code if the is a lot of it going on.
The more I think about the great questions that you asked the more I realize that I put myself in another quagmire.
Ultimately what is the goal? - To create an easy to use standard for using multiple types of very inexpensive MCUs with the propeller. I noticed that C and SimpleIDE don't have a place in that goal. If I remove these concepts it becomes quite simple to address all of the above.
I can just make a relatively dumb python GUI. Just select your target MCU, select a few options, and add any custom code. A little scripting and out pops a C file for that MCU. I can just assemble it as text. A little more scripting and I can have it compiled using the correct compiler and options for each MCU.
It would also need a spin loader program and a schematic to make any Prop into a programmer.
It won't happen overnight but this plan removes a lot of my self-imposed obstacles.
>main() function will run on the Propeller and that the functions main_mcu() and foo() will run on the attached MCU.
Sounds similar to how my Cooperative multitasking event machine works,
Main jumps to task, it don't use the stack between main and the (up to) 32tasks, just a mailbox that I guess you could send over i2c
outboxid:1 is milliseconds OS_sleep
outboxid:2 is seconds OS_sleep up to the year 2138
outboxid:3 still working on 3 to 254 id
Setting up a new task, only require you to add its name to common.h file, nothing else, no code spill over to main.c.
#include "common.h"
/* PUT YOUR TASK NAME HERE and also in common.h */
#define this test_task_name
/* TASK ENTRY */
void TASKFUNX (void) {
if (task[this].stateset){task[this].statedo = task[this].stateset; task[this].stateset = 0;}
switch (task[this].statedo){
case BOOT: SetEvent(this,START); break; // all task always run boot state at reset.
case INIT: break; // if different from boot state.
case START: OS_sleep(this,500); break // repeat this 1/2sec as an example, toggle your led etc
case EXIT:
default: break;
}
}
TASKFUNX and SetEvent are just macro's
Had to use define for task names as enum is not precompiled.
#ifndef SRC_COMMON_H_
#define SRC_COMMON_H_
#include "stdint.h" /* so uint32_t and true&false works */
/***************************************/
/* PUT TASK NAMES HERE 0-31 + End */
#define test_task_name 0
#define myother_name 1
#define TaskListEnd 2
/***************************************/
#define SetEvent(TASKn,STATEn) task[TASKn].stateset = STATEn; eventset |= 1<<TASKn
/* MACRO for task function name */
#define taskname2(x) TaskFunc ## x
#define taskname1(x) taskname2(x)
#define TASKFUNX taskname1(this)
...
I've been exploring the other potential uses for these MCUs than can benefit the propeller.
This post focuses on the N76E003
Here is what I have so far:
10Base-T Ethernet Transformer
8/16 bit parallel SRAM controller
TTL to DB9 by emulating the MAX3232 ic
16 channel multiplexer by adding a shift bit and 8 transistors, providing 16 outputs on 9 pins, emulating the CD74HC4067M96
8 bit parallel to UART, which I guess is basically just an 8 Bit Shift Register
and of course the 2D grid & decoder to UART
I think building a list of target projects will give me a wider set of requirements and help me structure the overall project.
What do you think? Is everything on the list possible? What questions do I need to ask and answer? What else can we add to the list to increase the usefulness of the project?
I've been exploring the other potential uses for these MCUs than can benefit the propeller.
This post focuses on the N76E003
I think building a list of target projects will give me a wider set of requirements and help me structure the overall project.
What do you think? Is everything on the list possible? What questions do I need to ask and answer? What else can we add to the list to increase the usefulness of the project?
8 bit parallel to UART, which I guess is basically just an 8 Bit Shift Register
If you mean UART to IO expansion, yes, that plus i2c to IO, would be widely useful.
I've not tried i2c on N76E003 yet, but the data seems to have good flow charts.
One very flexible idea, is to do a 'UART-SFR bridge', to allow users to R/W any SFR using address.data pair.
This can then do any GPIO tasks, as well as enable/configure PWM etc
Best focus is the fastest/simplest code overhead in the host, with smallest pin count.
In N76E003 + P1 pairing, I think that is 1MBd UART, tho 400kHz i2c is probably close behind.
I would start with UART Duplex (2 pins) as that is easy to test and highly portable.
Half-duplex could be supported, and it does save 1 pin, (N76E003 has nice support via UART0PX Serial port 0 pin exchange), but this is trickier to nail down, and is harder to test.
Also, I'll post below some rough notes I made in talking to a NuTiny-SDK-N76E003 - using FreeBASIC on PC. Needed a couple of magic steps....
This allows you to test using a PC.
FreeBASIC PC-Side Test code, for NuTiny-SDK-N76E003 Com port - note: this has some fussy details, not seen on other VCPs, but it can work, and with a good Baud range.
12M Virtual Baud clock, 16b divider, goes down to ~184 Baud. 1MBd is upper limit of N76E003 chip.
#include once "windows.bi"
#include "file.bi"
'Option Action
' 'CSn' Set the CTS duration (in ms) (n>=0), 0 = turn off, default = 1000
' 'DSn' Set the DSR duration (in ms) (n>=0), 0 = turn off, default = 1000
' 'CDn' Set the Carrier Detect duration (in ms) (n>=0), 0 = turn off
' 'OPn' Set the 'Open Timeout' (in ms) (n>=0), 0 = turn off
' 'TBn' Set the 'Transmit Buffer' size (n>=0), 0 = default, depends on platform
' 'RBn' Set the 'Receive Buffer' size (n>=0), 0 = default, depends on platform
' 'RS' Suppress RTS detection
' 'LF' Communicate in ASCII mode (add LF to every CR) - Win32 doesn't support this one
' 'ASC' same as 'LF'
' 'BIN' The opposite of LF and it'll always work
' 'PE' Enable 'Parity' check
' 'DT' Keep DTR enabled after CLOSE
' 'FE' Discard invalid character on error
' 'ME' Ignore all errors
' 'IRn' IRQ number for COM (only supported (?) on DOS)
'function comcheck(port as string) as integer
Dim As HANDLE ch
'=========================================================================
'
Dim As Integer res
dim as DWORD wb
Dim As String buff,comport
Dim com_lpOverlapped As LPOVERLAPPED
Dim As COMMTIMEOUTS ct
Dim As DCB rDCB 'you'll need to get/setcommstate..
'
' COM4 is SiLabs CP2102N == Sends ONE CHAR OK
' COM5 is Nuvoton VCP == Testing device
' COM7 is Nuvoton VCP on XP
'open com "COM5:3906,n,8,1,cs0,cd0,ds0,rs" as #1
'open com "COM5:3906,n,8,1,cs0,cd0,ds0,rs,bin" as #1
open com "COM5:3906,n,8,1,ds0,bin" as #1 '_needs_ DS0, or does not work on Nuvoton
'' Get the windows handle
ch = cast(HANDLE, Fileattr( 1, fbFileAttrHandle ))
res=GetCommState(ch,@rDCB)
'this will remain static after Set!
res=SetCommState(ch,@rDCB) ' can comment ALL the above, and works, until remove this line, then stops working....
print "SetCommState res:";res
res=GetCommState(ch,@rDCB)
'read back
print "rDCB DCBlength: ";rDCB.DCBlength ' DCBlength as DWORD
print "rDCB BaudRate ";rDCB.BaudRate ' BaudRate as DWORD
print "rDCB fBinary ";rDCB.fBinary ' fBinary : 1 as DWORD
print "rDCB fParity ";rDCB.fParity ' fParity : 1 as DWORD
print "rDCB fOutxCtsFlow ";rDCB.fOutxCtsFlow ' fOutxCtsFlow : 1 as DWORD
print "rDCB fOutxDsrFlow ";rDCB.fOutxDsrFlow ' fOutxDsrFlow : 1 as DWORD
print "rDCB fDtrControl ";rDCB.fDtrControl ' fDtrControl : 2 as DWORD
print "rDCB fDsrSensitivity ";rDCB.fDsrSensitivity ' fDsrSensitivity : 1 as DWORD
print "rDCB fTXContinueOnXoff ";rDCB.fTXContinueOnXoff ' fTXContinueOnXoff : 1 as DWORD
print "rDCB fOutX ";rDCB.fOutX ' fOutX : 1 as DWORD
print "rDCB fInX ";rDCB.fInX ' fInX : 1 as DWORD
print "rDCB fErrorChar ";rDCB.fErrorChar ' fErrorChar : 1 as DWORD
print "rDCB fNull ";rDCB.fNull ' fNull : 1 as DWORD
print "rDCB fRtsControl ";rDCB.fRtsControl ' fRtsControl : 2 as DWORD
print "rDCB fAbortOnError ";rDCB.fAbortOnError ' fAbortOnError : 1 as DWORD
print "rDCB fDummy2 ";rDCB.fDummy2 ' fDummy2 : 17 as DWORD
print "rDCB wReserved ";rDCB.wReserved ' wReserved as WORD
print "rDCB XonLim ";rDCB.XonLim ' XonLim as WORD
print "rDCB XoffLim ";rDCB.XoffLim ' XoffLim as WORD
print "rDCB ByteSize ";rDCB.ByteSize ' ByteSize as UBYTE
print "rDCB Parity ";rDCB.Parity ' Parity as UBYTE
print "rDCB StopBits ";rDCB.StopBits ' StopBits as UBYTE
print "rDCB XonChar ";rDCB.XonChar ' XonChar as byte
print "rDCB XoffChar ";rDCB.XoffChar ' XoffChar as byte
print "rDCB ErrorChar ";rDCB.ErrorChar ' ErrorChar as byte
print "rDCB EofChar ";rDCB.EofChar ' EofChar as byte
print "rDCB EvtChar ";rDCB.EvtChar ' EvtChar as byte
print "rDCB wReserved1 ";rDCB.wReserved1 ' wReserved1 as WORD
' ~~~~~~~~~~~~~~ Replace fields ~~~~~~~~~~~~~~~~~
'rDCB.fAbortOnError=1
'commstate.BaudRate =115200
'rDCB.BaudRate =3906
'rDCB.ByteSize =8
'rDCB.Parity = NOPARITY
'rDCB.StopBits = ONESTOPBIT
'rDCB.fBinary = TRUE
'rDCB.fParity = FALSE
'
#IFDEF Add_Queue_Mask
res=SetupComm( ch, 1024,128) ' JG added, no magic effect ?
print "SetupComm res:";res 'success = non-zero
'declare function SetupComm(byval hFile as HANDLE, byval dwInQueue as DWORD, byval dwOutQueue as DWORD) as WINBOOL
res=SetCommMask(ch, EV_RXCHAR + EV_TXEMPTY )
print "SetCommMask res:";res 'success = non-zero
res=GetCommTimeouts(ch,@ct)
print "GetComm res:";res 'success = non-zero
'
ct.ReadIntervalTimeout=0
ct.ReadTotalTimeoutMultiplier=0
ct.ReadTotalTimeoutConstant=100
ct.WriteTotalTimeoutMultiplier=100 'milliseconds allowed for each byte written
ct.WriteTotalTimeoutConstant=500 '+ milliseconds for total write time
'
res=SetCommTimeouts(ch,@ct)
print "SetComm res:";res 'success = non-zero
'
dim as COMSTAT chstat
dim as DWORD cherrsmask
ClearCommError(ch,@cherrsmask,@chstat)
' declare function ClearCommError(byval hFile as HANDLE, byval lpErrors as LPDWORD, byval lpStat as LPCOMSTAT) as WINBOOL
print "Clear res: ";res
'
'c has
' //printf("SetCommState - ");
' SetCommState(m_hCOMHandle, &dcb );
' //printf("SetCommMask - ");
' SetCommMask(m_hCOMHandle, EV_RXCHAR|EV_TXEMPTY );
' //printf("SetupComm - ");
' SetupComm( m_hCOMHandle, 1024,128) ;
'declare function SetupComm(byval hFile as HANDLE, byval dwInQueue as DWORD, byval dwOutQueue as DWORD) as WINBOOL
' //printf("PurgeComm - ");
' PurgeComm( m_hCOMHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
'res=PurgeComm(ch,PURGE_TXCLEAR)'clear Write only
res=PurgeComm(ch,PURGE_TXABORT + PURGE_RXABORT + PURGE_TXCLEAR + PURGE_RXCLEAR)'JG now same as C code
print "Purge res: ";res
#ENDIF ' Add_Queue_Mask
'
'
buff="UUUUU"
res=WriteFile(_
ch,_
strptr(buff),_
len(buff),_
@wb,_
null)
' declare function WriteFile(byval hFile as HANDLE, byval lpBuffer as LPCVOID, byval nNumberOfBytesToWrite as DWORD, byval lpNumberOfBytesWritten as LPDWORD, byval lpOverlapped as LPOVERLAPPED) as WINBOOL
'res=1 on success, even if write times outs??
'
'in theory this would timeout in 4*100+500 milliseconds
' and wb=0
'
print "Bytes written:";wb
'CloseHandle(ch)
'sleep
Close #1
sleep
I also found we needed at least Nu-Link_Command_Tool_V2.02.6629 for reliable command line links, and I see they now have Revision 2.03.6674 (Released 2017-12-11)
If you mean UART to IO expansion, yes, that plus i2c to IO, would be widely useful.
I've not tried i2c on N76E003 yet, but the data seems to have good flow charts.
Following this, Google finds this code, for N76E003.i2c, somewhat format mangled on the Chinese web page I found, but their i2c seems simple enough.
Code looks to make a 34 byte RAM array and R/W into that, - there is no address fields managed, so mention of "The protocol of I2C is same the "24LC64" is rather loose.
Seems to NACK when hits 34 limit, then restarts from 0.
Ideas:
Some minor tweaks could have this acting like a 24C256, (2 Address bytes), and able to boot a P1 from ~17k of code image. (18k-i2c_Slave overhead)
The N76E003 has useful ADC and PWM, and once it is i2c P1 connected, that is almost free, so can expand P1 ability.
eg some address not sent by P1.boot or run, could read/write SFRs for ADC.PWM etc in a i2c-SFR bridge (upper address 0xff perhaps?)
Trickier, but maybe possible, would be N76E003+SPI_Flash, where code for i2c.24C256 emulation actually reads SPI flash, for much larger code support...
Slave:
/* ------------------------------------------------ -------------------------------------------------- ------- */
/* N76E003_i2c_Slave_Web.c from http://bbs.21ic.com/icview-2382616-1-1.html */
/* Copyright (c) 2015 Nuvoton Technology Corp. All rights reserved. */
/* */
/* ------------------------------------------------ -------------------------------------------------- ------- */
//************************************************ ************************************************** *********
// Nuvoton Technoledge Corp.
// Website: http://www.nuvoton.com
// E-Mail: MicroC-8bit@nuvoton.com
// Date: May / 1/2015
//************************************************ ************************************************** *********
//************************************************ ************************************************** *********
// File Function: N76E003 I2C Slave demo code
//************************************************ ************************************************** *********
#include <stdio.h>
#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_Define.h"
//************************************************ ************************************************** *********
// N76E885-series I2C slave mode demo code, the Slave address = 0xA4
//
// _____________ _____________
// | N76E003 (M) | | N76E003 (S) |
// | SDA | <--------> | SDA |
// | (I2C_Master)| | (I2C_Slave) |
// | SCL | ---------> | SCL |
// |_____________| |_____________|
//
//
// The protocol of I2C is same the "24LC64"
//************************************************ ************************************************** *********
#define I2C_CLOCK 13 // JG: [s][/s] not used in Slave
#define EEPROM_SLA 0xA4
UINT8 data_received [34], data_num = 0;
// ================================================ ================================================== ======
void I2C_ISR (void) interrupt 6
{
switch (I2STAT)
{
case 0x00:
STO = 1;
break;
// Follows Figure 15.9. Flow and Status of Slave Receiver Mode
case 0x60:
AA = 1;
// P3 = 0x60;
break;
case 0x68:
P02 = 0;
while (1);
break;
case 0x80:
// P3 = 0x80;
data_received [data_num] = I2DAT;
data_num ++;
if (data_num == 34)
AA = 0;
else
AA = 1;
break;
case 0x88:
// P3 = 0x88;
data_received [data_num] = I2DAT;
data_num = 0;
AA = 1;
break;
case 0xA0:
// P3 = 0xA0;
AA = 1;
break;
// Follows Figure 15.10. Flow and Status of Slave Transmitter Mode (mostly)
case 0xA8:
// P3 = 0xA0;
I2DAT = data_received [data_num];
data_num ++;
AA = 1;
break;
case 0xB8:
// P3 = 0xB8;
I2DAT = data_received [data_num];
data_num ++;
AA = 1;
break;
case 0xC0:
AA = 1;
break;
case 0xC8:
// P3 = 0xC8;
AA = 1;
break;
}
SI = 0;
while (STO);
}
// ================================================ ================================================== ======
void Init_I2C (void)
{
P13_Quasi_Mode; // set SCL (P13) is Quasi mode
P14_Quasi_Mode; // set SDA (P14) is Quasi mode
SDA = 1; // set SDA and SCL pins high
SCL = 1;
set_P0SR_6; // set SCL (P06) is Schmitt triggered input select.
set_EI2C; // enable I2C interrupt by setting IE1 bit 0
set_EA;
I2ADDR = EEPROM_SLA; // define own slave address
set_I2CEN; // enable I2C circuit
set_AA;
}
// ================================================ ================================================== ======
void main (void)
{
Set_All_GPIO_Quasi_Mode;
/* Initial I2C function */
Init_I2C (); // initial I2C circuit
while (1);
/* =================== */
}
Master: (testing pair)
/* ------------------------------------------------ -------------------------------------------------- ------- */
/* N76E003_i2c_Master_Web.c from http://bbs.21ic.com/icview-2382616-1-1.html */
/* Copyright (c) 2016 Nuvoton Technology Corp. All rights reserved. */
/* */
/* ------------------------------------------------ -------------------------------------------------- ------- */
//************************************************ ************************************************** *********
// Nuvoton Technoledge Corp.
// Website: http://www.nuvoton.com
// E-Mail: MicroC-8bit@nuvoton.com
// Date: Apr / 29/2016
//************************************************ ************************************************** *********
//************************************************ ************************************************** *********
// File Function: N76E003 I2C master mode demo code, the Slave address = 0xA4
//
// _____________ _____________
// | N76E003 (M) | | N76E003 (S) |
// | SDA | <--------> | SDA |
// | (I2C_Master)| | (I2C_Slave) |
// | SCL | ---------> | SCL |
// |_____________| |_____________|
//
// The protocol of I2C is same the "24LC64"
//************************************************ ************************************************** *********
#include <stdio.h>
#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_Define.h"
#define I2C_CLOCK 13 // JG: Looks to be cut/paste from N76E885, as 22.1184M/(4*(13+1)) = 394971.428 The default N76E003 value is 9, for 16M/(4*(9+1)) = 400000
#define EEPROM_SLA 0xA4
#define EEPROM_WR 0
#define EEPROM_RD 1
#define ERROR_CODE 0x78
#define PAGE_SIZE 32
// ================================================ ================================================== ======
void Init_I2C (void)
{
// /* Set I2C clock rate */
I2CLK = I2C_CLOCK;
/* Enable I2C */
set_I2CEN;
}
// ================================================ ================================================== ======
void I2C_Error (void)
{
// P3 = I2STAT;
// P3 = ERROR_CODE;
while (1);
}
// ================================================ ================================================== ======
void I2C_Process (UINT8 u8DAT)
{
UINT32 u32Count;
// ------------------------------------------------ --------------------------------------------
// ---- Page Write ------------------------------------------ ----------------------------------
// ------------------------------------------------ --------------------------------------------
/* Step1 */
set_STA; /* Send Start bit to I2C EEPROM */
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x08) // Check status value after every step
I2C_Error ();
/* Step2 */
clr_STA; // STA = 0
I2DAT = (EEPROM_SLA | EEPROM_WR);
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x18)
I2C_Error ();
/* Step3 */
I2DAT = 0x00; // address high for I2C EEPROM
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x28)
I2C_Error ();
/* Step4 */
I2DAT = 0x00; // address low for I2C EEPROM
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x28)
I2C_Error ();
/* Step5 */
for (u32Count = 0; u32Count <PAGE_SIZE; u32Count ++)
{
I2DAT = u8DAT;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x28)
I2C_Error ();
u8DAT = ~ u8DAT;
}
// ------------------------------------------------ --------------------------------------------
// ---- Waitting the ready for I2C write -------------------------------------- ----------------
// ------------------------------------------------ --------------------------------------------
/* Step6 */
do {
set_STO;
clr_SI;
set_STA; // Check if no ACK is returned by EEPROM, it is under timed-write cycle
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x08) // Check status value after every step
I2C_Error ();
clr_STA;
I2DAT = (EEPROM_SLA | EEPROM_WR);
clr_SI;
while (! SI); // Check SI set or not
} while (I2STAT! = 0x18);
/* Step7 */
set_STO;
clr_SI;
while (STO); /* Check STOP signal */
// ------------------------------------------------ --------------------------------------------
// ---- Page Read ------------------------------------------ ----------------------------------
// ------------------------------------------------ --------------------------------------------
/* Step8 */
set_STA;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x08) // Check status value after every step
I2C_Error ();
/* Step9 */
I2DAT = (EEPROM_SLA | EEPROM_WR);
clr_STA;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x18)
I2C_Error ();
/* Step10 */
I2DAT = 0x00; // address high for I2C EEPROM
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x28)
I2C_Error ();
/* Step11 */
I2DAT = 0x00; // address low for I2C EEPROM
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x28)
I2C_Error ();
/* Step12 */
/* Repeated START */
set_STA;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x10) // Check status value after every step
I2C_Error ();
/* Step13 */
clr_STA; // STA needs to be cleared after START codition is generated
I2DAT = (EEPROM_SLA | EEPROM_RD);
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x40)
I2C_Error ();
/* Step14 */
for (u32Count = 0; u32Count <PAGE_SIZE-1; u32Count ++)
{
set_AA;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x50)
I2C_Error ();
if (I2DAT! = u8DAT)
I2C_Error ();
u8DAT = ~ u8DAT;
}
/* Step15 */
clr_AA;
clr_SI;
while (! SI); // Check SI set or not
if (I2STAT! = 0x58)
I2C_Error ();
/* Step16 */
set_STO;
clr_SI;
while (STO); /* Check STOP signal */
}
// ================================================ ================================================== ======
void main (void)
{
/* Note
MCU power on system clock is HIRC (22.1184 MHz), so Fsys = 22.1184 MHz (jg: true for N76E885, N76E003 is 16MHz)
*/
Set_All_GPIO_Quasi_Mode;
Init_I2C (); // initial I2C circuit
I2C_Process (0x55); /* I2C Master will send 0x55, 0xAA, .... to slave */
P0 = 0x00;
P3 = 0x00;
while (1);
/* =================== */
}
Possible, but the N76E003 is light on pins for this, the N76E616 in TQFP48 has more io, for expansion use.
This is true, my plan is to use the MCU only for the address and control pins. This will allow me to address quite a few modules with the 003.
I agree that the same function should be made for the 616 as well. I think the user should have the option to specify their needs and the software will select the minimum chip to meet the specs and provide the finished program and driver software all in one shot.
? MAX3232 is a RS232 level translator, N76E003 cannot do that, but it can connect to a MAX3232
I was expecting to hit at least one snag in this list. The MAX3232 is cheap enough that adding anything to the N76E003 is not financially viable,
but the question is, can we gain a benefit by connecting one to a MAX3232?
8 bit parallel to UART, which I guess is basically just an 8 Bit Shift Register
If you mean UART to IO expansion, yes, that plus i2c to IO, would be widely useful.
I've not tried i2c on N76E003 yet, but the data seems to have good flow charts.
One very flexible idea, is to do a 'UART-SFR bridge', to allow users to R/W any SFR using address.data pair.
This can then do any GPIO tasks, as well as enable/configure PWM etc
I like this the best as it covers all of GPIO tasks above.
I really like the idea of a decoded character output buffer. As you and Peter put it I can just drop the pin and collect the data.
Best focus is the fastest/simplest code overhead in the host, with smallest pin count.
In N76E003 + P1 pairing, I think that is 1MBd UART, tho 400kHz i2c is probably close behind.
I would like to include all protocols that make sense, potentially including 4/8 bit parallel as well. The first focus for any project should be UART though.
I would start with UART Duplex (2 pins) as that is easy to test and highly portable.
Half-duplex could be supported, and it does save 1 pin, (N76E003 has nice support via UART0PX Serial port 0 pin exchange), but this is trickier to nail down, and is harder to test.
Also, I'll post below some rough notes I made in talking to a NuTiny-SDK-N76E003 - using FreeBASIC on PC. Needed a couple of magic steps....
This allows you to test using a PC.
FreeBASIC PC-Side Test code, for NuTiny-SDK-N76E003 Com port - note: this has some fussy details, not seen on other VCPs, but it can work, and with a good Baud range.
12M Virtual Baud clock, 16b divider, goes down to ~184 Baud. 1MBd is upper limit of N76E003 chip.
#include once "windows.bi"
#include "file.bi"
'Option Action
' 'CSn' Set the CTS duration (in ms) (n>=0), 0 = turn off, default = 1000
' 'DSn' Set the DSR duration (in ms) (n>=0), 0 = turn off, default = 1000
' 'CDn' Set the Carrier Detect duration (in ms) (n>=0), 0 = turn off
' 'OPn' Set the 'Open Timeout' (in ms) (n>=0), 0 = turn off
' 'TBn' Set the 'Transmit Buffer' size (n>=0), 0 = default, depends on platform
' 'RBn' Set the 'Receive Buffer' size (n>=0), 0 = default, depends on platform
' 'RS' Suppress RTS detection
' 'LF' Communicate in ASCII mode (add LF to every CR) - Win32 doesn't support this one
' 'ASC' same as 'LF'
' 'BIN' The opposite of LF and it'll always work
' 'PE' Enable 'Parity' check
' 'DT' Keep DTR enabled after CLOSE
' 'FE' Discard invalid character on error
' 'ME' Ignore all errors
' 'IRn' IRQ number for COM (only supported (?) on DOS)
'function comcheck(port as string) as integer
Dim As HANDLE ch
'=========================================================================
'
Dim As Integer res
dim as DWORD wb
Dim As String buff,comport
Dim com_lpOverlapped As LPOVERLAPPED
Dim As COMMTIMEOUTS ct
Dim As DCB rDCB 'you'll need to get/setcommstate..
'
' COM4 is SiLabs CP2102N == Sends ONE CHAR OK
' COM5 is Nuvoton VCP == Testing device
' COM7 is Nuvoton VCP on XP
'open com "COM5:3906,n,8,1,cs0,cd0,ds0,rs" as #1
'open com "COM5:3906,n,8,1,cs0,cd0,ds0,rs,bin" as #1
open com "COM5:3906,n,8,1,ds0,bin" as #1 '_needs_ DS0, or does not work on Nuvoton
'' Get the windows handle
ch = cast(HANDLE, Fileattr( 1, fbFileAttrHandle ))
res=GetCommState(ch,@rDCB)
'this will remain static after Set!
res=SetCommState(ch,@rDCB) ' can comment ALL the above, and works, until remove this line, then stops working....
print "SetCommState res:";res
res=GetCommState(ch,@rDCB)
'read back
print "rDCB DCBlength: ";rDCB.DCBlength ' DCBlength as DWORD
print "rDCB BaudRate ";rDCB.BaudRate ' BaudRate as DWORD
print "rDCB fBinary ";rDCB.fBinary ' fBinary : 1 as DWORD
print "rDCB fParity ";rDCB.fParity ' fParity : 1 as DWORD
print "rDCB fOutxCtsFlow ";rDCB.fOutxCtsFlow ' fOutxCtsFlow : 1 as DWORD
print "rDCB fOutxDsrFlow ";rDCB.fOutxDsrFlow ' fOutxDsrFlow : 1 as DWORD
print "rDCB fDtrControl ";rDCB.fDtrControl ' fDtrControl : 2 as DWORD
print "rDCB fDsrSensitivity ";rDCB.fDsrSensitivity ' fDsrSensitivity : 1 as DWORD
print "rDCB fTXContinueOnXoff ";rDCB.fTXContinueOnXoff ' fTXContinueOnXoff : 1 as DWORD
print "rDCB fOutX ";rDCB.fOutX ' fOutX : 1 as DWORD
print "rDCB fInX ";rDCB.fInX ' fInX : 1 as DWORD
print "rDCB fErrorChar ";rDCB.fErrorChar ' fErrorChar : 1 as DWORD
print "rDCB fNull ";rDCB.fNull ' fNull : 1 as DWORD
print "rDCB fRtsControl ";rDCB.fRtsControl ' fRtsControl : 2 as DWORD
print "rDCB fAbortOnError ";rDCB.fAbortOnError ' fAbortOnError : 1 as DWORD
print "rDCB fDummy2 ";rDCB.fDummy2 ' fDummy2 : 17 as DWORD
print "rDCB wReserved ";rDCB.wReserved ' wReserved as WORD
print "rDCB XonLim ";rDCB.XonLim ' XonLim as WORD
print "rDCB XoffLim ";rDCB.XoffLim ' XoffLim as WORD
print "rDCB ByteSize ";rDCB.ByteSize ' ByteSize as UBYTE
print "rDCB Parity ";rDCB.Parity ' Parity as UBYTE
print "rDCB StopBits ";rDCB.StopBits ' StopBits as UBYTE
print "rDCB XonChar ";rDCB.XonChar ' XonChar as byte
print "rDCB XoffChar ";rDCB.XoffChar ' XoffChar as byte
print "rDCB ErrorChar ";rDCB.ErrorChar ' ErrorChar as byte
print "rDCB EofChar ";rDCB.EofChar ' EofChar as byte
print "rDCB EvtChar ";rDCB.EvtChar ' EvtChar as byte
print "rDCB wReserved1 ";rDCB.wReserved1 ' wReserved1 as WORD
' ~~~~~~~~~~~~~~ Replace fields ~~~~~~~~~~~~~~~~~
'rDCB.fAbortOnError=1
'commstate.BaudRate =115200
'rDCB.BaudRate =3906
'rDCB.ByteSize =8
'rDCB.Parity = NOPARITY
'rDCB.StopBits = ONESTOPBIT
'rDCB.fBinary = TRUE
'rDCB.fParity = FALSE
'
#IFDEF Add_Queue_Mask
res=SetupComm( ch, 1024,128) ' JG added, no magic effect ?
print "SetupComm res:";res 'success = non-zero
'declare function SetupComm(byval hFile as HANDLE, byval dwInQueue as DWORD, byval dwOutQueue as DWORD) as WINBOOL
res=SetCommMask(ch, EV_RXCHAR + EV_TXEMPTY )
print "SetCommMask res:";res 'success = non-zero
res=GetCommTimeouts(ch,@ct)
print "GetComm res:";res 'success = non-zero
'
ct.ReadIntervalTimeout=0
ct.ReadTotalTimeoutMultiplier=0
ct.ReadTotalTimeoutConstant=100
ct.WriteTotalTimeoutMultiplier=100 'milliseconds allowed for each byte written
ct.WriteTotalTimeoutConstant=500 '+ milliseconds for total write time
'
res=SetCommTimeouts(ch,@ct)
print "SetComm res:";res 'success = non-zero
'
dim as COMSTAT chstat
dim as DWORD cherrsmask
ClearCommError(ch,@cherrsmask,@chstat)
' declare function ClearCommError(byval hFile as HANDLE, byval lpErrors as LPDWORD, byval lpStat as LPCOMSTAT) as WINBOOL
print "Clear res: ";res
'
'c has
' //printf("SetCommState - ");
' SetCommState(m_hCOMHandle, &dcb );
' //printf("SetCommMask - ");
' SetCommMask(m_hCOMHandle, EV_RXCHAR|EV_TXEMPTY );
' //printf("SetupComm - ");
' SetupComm( m_hCOMHandle, 1024,128) ;
'declare function SetupComm(byval hFile as HANDLE, byval dwInQueue as DWORD, byval dwOutQueue as DWORD) as WINBOOL
' //printf("PurgeComm - ");
' PurgeComm( m_hCOMHandle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
'res=PurgeComm(ch,PURGE_TXCLEAR)'clear Write only
res=PurgeComm(ch,PURGE_TXABORT + PURGE_RXABORT + PURGE_TXCLEAR + PURGE_RXCLEAR)'JG now same as C code
print "Purge res: ";res
#ENDIF ' Add_Queue_Mask
'
'
buff="UUUUU"
res=WriteFile(_
ch,_
strptr(buff),_
len(buff),_
@wb,_
null)
' declare function WriteFile(byval hFile as HANDLE, byval lpBuffer as LPCVOID, byval nNumberOfBytesToWrite as DWORD, byval lpNumberOfBytesWritten as LPDWORD, byval lpOverlapped as LPOVERLAPPED) as WINBOOL
'res=1 on success, even if write times outs??
'
'in theory this would timeout in 4*100+500 milliseconds
' and wb=0
'
print "Bytes written:";wb
'CloseHandle(ch)
'sleep
Close #1
sleep
I also found we needed at least Nu-Link_Command_Tool_V2.02.6629 for reliable command line links, and I see they now have Revision 2.03.6674 (Released 2017-12-11)
Ideas:
Some minor tweaks could have this acting like a 24C256, (2 Address bytes), and able to boot a P1 from ~17k of code image. (18k-i2c_Slave overhead)
The N76E003 has useful ADC and PWM, and once it is i2c P1 connected, that is almost free, so can expand P1 ability.
eg some address not sent by P1.boot or run, could read/write SFRs for ADC.PWM etc in a i2c-SFR bridge (upper address 0xff perhaps?)
Trickier, but maybe possible, would be N76E003+SPI_Flash, where code for i2c.24C256 emulation actually reads SPI flash, for much larger code support...
I think a lot of people would be happy if we made this. I could certainly use it
The problem I found with trying to do any kind of "universal" solution is that it is all very good and well if it were a chip available in DIP as well as SMD, preprogrammed, and available from most suppliers very cheap. Internally with my own designs I don't have any problems with designing my special peripheral micros onto a board and having programming pads/vias/headers to Flash them with etc.
However I was very confused with your post because I think you are confused about this area of technology. I didn't know where to start to reply since the actual statements are incorrect or too open-ended.
I bought 10 of these to play around with. Part of my pile of tiny christmas presents that I bought for myself this year.
Interesting little break-out board. Even has a 3v3 regulator, not mandatory on N76E003 as it runs just fine on USB-5V, but useful for Rasp-Pi or P1 testing where you want 3v3.
Comments
The N76E003 has a 400kHz i2c slave hardware block, so i2c could easily be added as a supported link.
(The State Logic looks to have 5 states for Slave receiver, and 6 states for slave transmitter mode, so code will be small)
i2c needs 2 wires, but has an advantage you can clip-on many peripherals to the same bus.
I'd have to agree. The current focus is on a few specific controllers, considered for specific price and capabilities. The target language is C to begin with, but all languages will be considered eventually.
That said I hope there will be overlap in both this project and yours and that we can help each other.
Are you using i2c to allow for multiple devices on the same bus?
@jmg can UART in half duplex can use addressing to reach multiple devices on one wire as well as i2c?
-Phil
That is what I thought I had read. Thanks for the info
The UART has a choice of 8b and 9b modes, (9b is a little less PC friendly, as PC-UARTS cannot quickly wiggle the mark/space parity, unless you buy the EXAR ones that supports 9b native )
You can easily allocate those 8-9 bits as any address/command mix.
Perhaps something like
RB8=1 :: Address_7 bits + 1b Echo
RB8=0 :: Data payload for Peripheral configuration
Sent as E.aaaaaaa.RB8
eg
1.aaaaaaa.1 - Immediate Echo selected 1 of 128 peripheral from slave aaaaaaa
0.aaaaaaa.1 - Address Slave aaaaaaa, multi byte command, no echo - follows with cccccccc.0, ddddddd.0, etc
You do need to set the address value somewhere - it can be compile time, or spare pins, or ADC to 2 resistors, or a mix of these.
If you want to work with any PC-UART, then you can drop to 8b, and (eg) have a 6 bit address field, or maybe 7b can still be squeezed in via a prefix approach
1.aaaaaaa - Immediate echo of 1 of 127 peripheral data byte(s)
1.0000000, 1.aaaaaaa - Command prefix tells all slaves do not echo following next address - command/config info will follow.
Send any number of 0.ddddddd 7 bit payload config bytes, until terminate with Address token (1.aaaaaaa)
-or-
For MCU-networks, we have also used a UART-RING topology. This uses 2 pins on every node, but now address management is simply by physical loop location.
I call this a twisted-ring, using the 9th bit as an frame edge-flag, and a simple common rule is N bytes after 9b _/= are extracted/replaced, with 9th bit cleared.
All other bytes are simply passed along.
Result is first slave removes 4 bytes, and echos 4 byte reply, with 9th bit low, moving the frame edge for NEXT slave along 4 bytes.
Slaves can be counted/discovered by checking delay effect, and you have a natural integrity check.
eg Knowing < 50 nodes, 4B each, send 200 bytes 9b=0, then start sending 9b=1. 9 slaves will return 36 bytes echos with 9b=0, and then 37th byte+ has 9b=1
However most micros expect the 9th bit to be high when it sees an address and can interrupt the CPU or automatically match and enable it. The state of the 9th bit was an unfortunate design decision at the time but one we need to work in with.
I don't bother using slow open-collector though as all we are trying to do is to protect I/O pins, not arbitrate, in which case it is easier to add a series resistor, and 100R is sufficiently high enough for that purpose. Some of my early 9th bit network designs were in fire control panels which were quite large so using only single wire was quite useful for this purpose. For anything outside of that and up through the many levels I used RS485. I even had RS485 working in an early graphic elevator display over untwisted fire-proof cable that snaked up and down through the levels and from lift well to lift well, low rise section to high rise section, and to make it work on the one continuous cable I just simply reduced the speed to 19.2k.
But 9th bit mode doesn't have to be all individual addresses. I use some for global and group addresses where I don't expect a response but they should all listen, and some other codes for network control,
I2C works well when it it is all very close, it was after all designed for "Inter Integrated Circuit" (IIC which became I2C) operation whereby Philips TV at the time could have subsystems such as the tuner for instance distributed in the same chassis. Nonetheless I have been guilty of running this bus many scores of meters in the distant past
To clarify, several modes of UART, as well as I2C, USB, PWM, and PIC will need to be supported.
Would this be a better tools with a wide net cast over everything or could it benefit from prioritizing the common or useful modes over any esoteric options?
Not sure what you meant by 'UART modes', but the big variables around UART use are
a) BAUD rate
b) Bits Sent
and up a layer
c) Bytes in transaction
BAUD rate is quite easy to change, over a very wide range in the N76E003. ie 1MBd, to way down to something no PC can send, but an eyeball might decode
That's usually a single define, with some range tests if needed for init code.
Bits Sent is somewhat more structural - 9 bit modes have benefits, especially MCU-MCU, but will not work at top baud speeds from a PC, without a special device selection (EXAR)
PCs can work well enough to develop and test 9 bit code, using force-parity, as Peter mentions.
i2c slave is not too large a jump, and N76E003 has i2c Slave HW to 400k Baud.
USB is quite different, and bumps you into the SiLabs part, but there the 40k code size does open many doors.
Single-bridge parts usually need ~ 8k of code for a good set of features, so a 40k part could support a number.
eg UART-Bridge and i2c or SPI Bridge, that appears as 2 COM ports may be possible.
Not sure what your PWM meant, in peripheral comms-link context, but it would be possible to have a command group to set up PWM Frequency and duty, for LED or Buzzer additions to a keyboard code.
It would be possible to share pins for LED Drive and keyboard scan, with minor caveats.
N76E003 has quite comprehensive PWM, so could drive up to 6 Servos, or 6 LEDs with up to 16b setpoints. 10b~8b would give 16kHz~64kHz for DAC use.
Piezo element drive would also be well supported.
Some years ago I build a small home automation system with PIC16F628 nodes networked using a MAX485 in half duplex. SNAP from http://www.hth.com/snap/ was the protocol used to comunicate between the nodes and a "master" PC. The system is still working, unfortunately I don't know where the sources are buried.
When I heard SNAP I immediately thought of Subnetwork Access Protocol which also came out in 1998.
What advantages did your automation system gain by using SNAP over other protocols?
I see there are a number of error detection methodology, and the line "Requires minimal MCU resources to implement" gets my attention.
I don't think there is a hardware implementation of this. My reading says TI supports the powerline modem concept with an RS-485 network.
Do you think this is something worth implementing in software?
With 8 address bits you can easily address loads of devices on RS-485. What would be the potential gain?
I think there may be a couple nice features that could be extracted in concept for this project which would likely be the same as the answer(s) to my first question.
The PIC is out of scope for this project but if you benefited from any specific feature(s), that could be useful here.
To send a [DLE] as data, just send [DLE] [DLE].
I've used this technique in several applications.
-Phil
SNAP seems to allocate a SYNC byte value, but takes no efforts to ensure DATA/CRC or command bytes are NOT equal to that value.
I guess you could add a link monostable for additional sync protection, but they do not mention that ?
It also has quite high overhead - a single byte payload example, is 9 bytes out, and 8 bytes for ACK/NAK - a bit much for IO/keypad-like peripherals, but OK for wider networks like Home Automation ?
I agree, I think the most to extract from that would be a low-resource feature or two which could be implemented as options in this software. @macca has experience enough to help us identify those features, if any.
I'm going to take a closer look at the error detection. With either target MCU I think minimal error detection beyond the parity bit may be a necessary option for some high precision applications. It may be as simple as triple sending the data but there are better ways. I'm thinking XOR logic may be used in this option.
I think it could be this simple to program a 2D grid with UART communications.
You just compile this and run it on your propeller with the subMCU connected on a breadboard or something to program the subMCU. Your results and the subMCU status will print to your console.
I'm sure I've missed stuff, but what do you all think?
Forgive the keyboard concept. It's just an example. The point is more about the code structure and the benefit of standardizing a few MCUs so propeller users can focus on propeller code. I'll get some use out of it as well.
Not really proposing it as new. I cited an example and asked if a similar concept existed for propeller.
Other facilities can be included for all the servos and sensors that propeller users already use allowing them to free up pins, cogs, or both with a minimal effort.
That is an interesting concept.
The missing part is: How on Earth are you going to make that happen?
That would require some super compiler/build system that can:
1) Analyse your source code, somehow recognize the parts that should run on the Propeller and those parts intended run on the attached MCU.
2) Use different compilers to build binary code form those extracted parts that targets the Propeller and attached MCU.
3) Probably have more than just two compilers available as the MCU could be of any architecture.
4) Down load the compiled binaries to the various processors and get them running.
That is a great idea. I have never seen a system that can do that. It sounds like a lot of work.
Typically people build systems out of different architecture processors. They have separate source code files for each device. Perhaps in different projects/sub projects. They have customized build systems to invoke the appropriate compilers and other tools for each part.
All those global variables have to go.
A common way to get the same source file to compile and build for different machines is to make use of #ifdef. The #ifdef and #if can be used to select which parts of the code are to be used in different places. That works when there are a few little things that need tweaking from platform platform. I can get very messy and make for unreadable code if the is a lot of it going on.
Thanks, that gives me some great stuff to research for my second go in a bit.
The more I think about the great questions that you asked the more I realize that I put myself in another quagmire.
Ultimately what is the goal? - To create an easy to use standard for using multiple types of very inexpensive MCUs with the propeller. I noticed that C and SimpleIDE don't have a place in that goal. If I remove these concepts it becomes quite simple to address all of the above.
I can just make a relatively dumb python GUI. Just select your target MCU, select a few options, and add any custom code. A little scripting and out pops a C file for that MCU. I can just assemble it as text. A little more scripting and I can have it compiled using the correct compiler and options for each MCU.
It would also need a spin loader program and a schematic to make any Prop into a programmer.
It won't happen overnight but this plan removes a lot of my self-imposed obstacles.
Sounds similar to how my Cooperative multitasking event machine works,
Main jumps to task, it don't use the stack between main and the (up to) 32tasks, just a mailbox that I guess you could send over i2c
outboxid:1 is milliseconds OS_sleep
outboxid:2 is seconds OS_sleep up to the year 2138
outboxid:3 still working on 3 to 254 id Setting up a new task, only require you to add its name to common.h file, nothing else, no code spill over to main.c.
TASKFUNX and SetEvent are just macro's
Had to use define for task names as enum is not precompiled.
Yup or the multitasking Bypic.
This is the direction I have chosen, to complement the Prop.
I don't care about a coupla quid, my time is valuable.
Cool stuff. Care to share a project layout and how the multitasking benefited your application, please? That would be a great contribution here :-D
I've been exploring the other potential uses for these MCUs than can benefit the propeller.
This post focuses on the N76E003
Here is what I have so far:
10Base-T Ethernet Transformer
8/16 bit parallel SRAM controller
TTL to DB9 by emulating the MAX3232 ic
16 channel multiplexer by adding a shift bit and 8 transistors, providing 16 outputs on 9 pins, emulating the CD74HC4067M96
8 bit parallel to UART, which I guess is basically just an 8 Bit Shift Register
and of course the 2D grid & decoder to UART
I think building a list of target projects will give me a wider set of requirements and help me structure the overall project.
What do you think? Is everything on the list possible? What questions do I need to ask and answer? What else can we add to the list to increase the usefulness of the project?
Comments added
Not sure what you mean here - do you mean use the transformer as isolation, on come custom serial link ?
Possible, but the N76E003 is light on pins for this, the N76E616 in TQFP48 has more io, for expansion use.
? MAX3232 is a RS232 level translator, N76E003 cannot do that, but it can connect to a MAX3232
General IO expansion is always good,
If you mean UART to IO expansion, yes, that plus i2c to IO, would be widely useful.
I've not tried i2c on N76E003 yet, but the data seems to have good flow charts.
One very flexible idea, is to do a 'UART-SFR bridge', to allow users to R/W any SFR using address.data pair.
This can then do any GPIO tasks, as well as enable/configure PWM etc
Yes, keypad encode is common.
Best focus is the fastest/simplest code overhead in the host, with smallest pin count.
In N76E003 + P1 pairing, I think that is 1MBd UART, tho 400kHz i2c is probably close behind.
I would start with UART Duplex (2 pins) as that is easy to test and highly portable.
Half-duplex could be supported, and it does save 1 pin, (N76E003 has nice support via UART0PX Serial port 0 pin exchange), but this is trickier to nail down, and is harder to test.
Also, I'll post below some rough notes I made in talking to a NuTiny-SDK-N76E003 - using FreeBASIC on PC. Needed a couple of magic steps....
This allows you to test using a PC.
FreeBASIC PC-Side Test code, for NuTiny-SDK-N76E003 Com port - note: this has some fussy details, not seen on other VCPs, but it can work, and with a good Baud range.
12M Virtual Baud clock, 16b divider, goes down to ~184 Baud. 1MBd is upper limit of N76E003 chip.
I also found we needed at least Nu-Link_Command_Tool_V2.02.6629 for reliable command line links, and I see they now have Revision 2.03.6674 (Released 2017-12-11)
Following this, Google finds this code, for N76E003.i2c, somewhat format mangled on the Chinese web page I found, but their i2c seems simple enough.
Code looks to make a 34 byte RAM array and R/W into that, - there is no address fields managed, so mention of "The protocol of I2C is same the "24LC64" is rather loose.
Seems to NACK when hits 34 limit, then restarts from 0.
Ideas:
Some minor tweaks could have this acting like a 24C256, (2 Address bytes), and able to boot a P1 from ~17k of code image. (18k-i2c_Slave overhead)
The N76E003 has useful ADC and PWM, and once it is i2c P1 connected, that is almost free, so can expand P1 ability.
eg some address not sent by P1.boot or run, could read/write SFRs for ADC.PWM etc in a i2c-SFR bridge (upper address 0xff perhaps?)
Trickier, but maybe possible, would be N76E003+SPI_Flash, where code for i2c.24C256 emulation actually reads SPI flash, for much larger code support...
Slave:
Master: (testing pair)
Addit:
Another link on the Nuvoton website leads to
https://github.com/OpenNuvoton/NuMicro-8051-Family/blob/master/README.md
with code in both IAR and Keil - for casual users, the IAR demo sounds better : 4k vs 2k code size, and no code-offsets.
The SiLabs toolchain, has a full version of Keil compiler (no low code limits).
There is also SDCC compiler : http://sdcc.sourceforge.net/index.php#News
I think a lot of people would be happy if we made this. I could certainly use it
However I was very confused with your post because I think you are confused about this area of technology. I didn't know where to start to reply since the actual statements are incorrect or too open-ended.
Interesting little break-out board. Even has a 3v3 regulator, not mandatory on N76E003 as it runs just fine on USB-5V, but useful for Rasp-Pi or P1 testing where you want 3v3.