Sunday, November 20, 2016

Video, keyboard, sound for MKHBC-8-Rx.

Update on MKHBC-8-Rx project.

While I still need to put RTC + Banked RAM circuit to the more permanent prototyping board (currently on a breadboard), I am already thinking about next step - a user I/O for my system, so it becomes a fully self contained system without the need of a PC host.
I recently re-discovered Parallax Propeller chip and it seems to be a very good candidate for this application.
With 8 independent cores (called cogs in Parallax documentation), fast 32-bit architecture and built in video generating capabilities, it should make the task not too difficult.
I already had a QuickStart board from Parallax, which is a starter/demo board for showing of Parallax Propeller (P8X32A) chip capabilities.
The board is equipped with a header providing access to all GPIO pins. I purchased a Human Interface Board/shield for QuickStart that has VGA port, PS/2 keyboard/mouse ports and other features and started experimenting.
First I interfaced my retro keyboard controller to it. You may read about my TI99/4A keyboard controller in my earlier blog updates (titled: "I2C keyboard controller"), although I simplified the firmware as I didn't need I2C support in this particular application. Now it is a much simpler serial keyboard similar to PS/2 but it is not a PS/2 compatible protocol, just voltage levels and pins are PS/2-like. The driver for Propeller chip was easy to implement and this experiment was a success. I could use PS/2 interface on Human Interface board to hook my keyboard up to the Propeller.
Next I went a step further and created a character terminal application out of this setup.
I since switched to Propeller Project Board USB with my terminal application, after adding VGA, keyboard and serial port sockets to it. The software remains the same.
When I add mass storage capability (micro-SD card) to the application, I shall have a fully standalone computer system that will not need a host PC to upload software. The software will be transferred via files on micro-SD card.

On the pictures below my first experiments with composite video and making my keyboard controller talk to Parallax board:


This screenshot shows working character terminal with video generated by Parallax Propeller on VGA display. On the bottom an experimental code of terminal menu, which I will use to invoke various special functions, like terminal setup or access to mass storage:


The character terminal application on Parallax Project board:


I will publish the software for Parallax Propeller once it is finished.

11/21/2016
M.K.

Tuesday, August 23, 2016

The iRobot Create and STM32 Nucleo F411RE board.


It's been a while since I did any project related to robotics. I have never done anything advanced, but some time ago (about 10 years) me and few friends from work got interested in the topic of making our own autonomous robot and programming it for fun and ultimately entering some competition with it.
We have never got that far, but what we ended up with was buying iRobot Create and Qwerk 1.2b robotic controller module from Charmed Labs for a considerable cost (at that time all that single board cheap computers, boards and shields were not as readily and cheaply available as they are today).
I don't think that Charmed Labs carry this module anymore. It was a single board computer powered by 200 MHz ARM9 RISC processor and Linux O.S. with multitude of I/O pins already built in and useful for robotic applications (digital I/O, analog I/O, servos, motors). It's been advertised as a Telepresence Robot Kit (TeRK). On top of Linux ran a server that used CORBA like protocol (ICE) to which client application written in Java, C# or C++ could connect remotely to control the hardware connected to the controller.

Here is the actual module that I still have:


and below are some of our (mostly mine as my friends lost interest quite quickly) clumsy attempts at making a robot based on iRobot Create platform and the Qwerk controller:


That robot's main function was to autonomously ride around the house, avoid obstacles and take pictures. It could also talk. The iRobot Create has its own controller and default programs for navigating autonomously of course, because it is basically Samba vacuum cleaner with the vacuum hardware removed to make a space/cargo bay for a controller, sensors and other junk to be able to piggy back on it. Thanks to iRobot's Open Interface, accessible via serial port, the platform could be controlled by external computer which was desirable and much more interesting than just running built in programs of a robotic vacuum cleaner.

Anyway, long story short, the Qwerk controller proved to be buggy and clumsy to use. It crashed constantly, WiFi often froze, etc. I ran out of ideas and didn't have proper skills to develop anything more interesting out of this robot.
After few more failed attempts at image (from camera) processing and light following (servo turret, now I have enough knowledge to know that this should be done with PID method) I finally got bored with it and moved on to other projects. Robot has been dismantled and stored.

In recent days I took the iRobot Create out of the box and charged it. The plan is to use one of the modern (Arduino or similar) SBC-s to control it via iRobot's Open Interface. I had an unused and untested yet STM32 Nucleo F411RE board. With some extra hardware and mbed IDE I have been able to write short program that sends data via Open Interface to play short melody via robot's internal speaker. That's a start. I will expand upon that code by implementing more commands and ultimately a library to create a skeleton for an autonomous robot, similar to the one I made before. I want it to be able to follow the light, ask to be recharged, avoid obstacles and bring me beer from the fridge (well, maybe this is too far for my skill, but who knows where this project will lead me?)

Here is the code:

#include "mbed.h"

Serial pc(PB_6, PB_7);  // TX, RX : UART1
DigitalOut myled1(LED1);

/*
    Hardware:
    
    STM32 Nucleo-F411RE board.
    RS232-TTL module.
    DB9 NULL MODEM connector.
    DB9 GENDER CHANGER.
    iRobot Create.
    iRobot Serial Mini DIN to DB9 converter/adapter.

    Connections:
    
    Pin PB_6 (TX, CN5.3, Arduino D10) -> RS232-TTL RXD
    Pin PB_7 (RX, CN7.21, Morpho)     -> RS232-TTL TXD
    Pin GND  (CN7.22, Morpho)         -> RS232-TTL GND
    Pin +5V  (CN7.18, Morpho)         -> RS232-TTL VCC
    iRobot Create Mini DIN -> Serial Converter -> NULL MODEM -> 
    -> Gender Changer -> RS232

*/

int main() {
  // play melody on iRobot Create every 10 seconds via Open Interface
  int data[20] = {128, 132, 140, 0, 4, 62, 12, 66, 12, 69, 12, 74, 36, 141, 0, 
                    -1};
  pc.baud(57600);
  wait(1.0);
  myled1 = 0;
  while(1) {
    int i = 0;
    while(data[i] >= 0) {
      pc.putc(data[i]);
      i++;
    }
    wait(10.0);
    myled1 = !myled1.read();
  }
}


The RTOS variant of the code which executes 3 tasks in parallel:

#include "mbed.h"
#include "rtos.h"

#define NL "\n\r"

using namespace rtos;

void print_char(char c = '*')
{
    printf("%c", c);
    fflush(stdout);
}

DigitalOut led1(LED1);

void print_thread(void const *argument)
{
    int n = 20;
    while (true) {
        Thread::wait(1000);
        print_char();
        n--;
        if (n == 0) {
            n = 20;
            printf(NL "... and again ..." NL);
        }
    }
}

Serial pc(PB_6, PB_7);  // TX, RX : UART1
int data[20] = {128, 132, 140, 0, 4, 62, 12, 66, 12, 69, 12, 74, 36, 141, 0, 
                    -1};

void irobot_thread(void const *argument)
{
  pc.baud(57600);
  Thread::wait(1000);
  while(1) {
    int i = 0;
    while(data[i] >= 0) {
      pc.putc(data[i]);
      i++;
    }
    Thread::wait(60000);
  }    
}

int main()
{
    printf(NL NL "*** RTOS basic example ***" NL);
    Thread t1(print_thread);
    Thread t2(irobot_thread);
    while (true) {
        led1 = !led1;
        Thread::wait(125);
    }
}

and some pics and a video:

video

Hopefully I will find time for this project and some more updates will come soon.
Thank you for visiting.

M.K.
8/24/2016

Sunday, February 7, 2016

Update on MKHBC-8-R2 6502 computer - prototyping banked RAM - revision 2.

I promised an update when I figure out the solution to my problem with RAM bank switching side effect when reading from the I/O mapped area $C000..$C0FF.
Well, I think I fixed it, but the glue logic is a bit more elaborate than I expected. Since the desired function was to generate rising edge on CLK input of 74LS374 latch only when /IO0 = 0 and R/W = 0 and Phi2 goes from HI to LO, I had to some up with the circuit with following truth table:

/IO0    R/W   Phi2    |    CLK
----------------------------------
  0      0      0     |     1
  0      0      1     |     0
  0      1      0     |     0
  0      1      1     |     0
  1      0      0     |     0
  1      0      1     |     0
  1      1      0     |     0
  1      1      1     |     0
----------------------------------

I'm sure you can see a familiar pattern here. This is like 3-input AND with output flipped over. Above function is realized with 3-input AND with its inputs negated:

Since I have no IC with 3-input AND, I transformed that circuit to NAND-s only equivalent and came up with this:

It looks like I will be needing this extra 74LS00 chip that I saved (see my previous blog update). Here is the final circuit for banked RAM:


Now the bank register is not switching to bank 0 when reading operation is performed on address from I/O 0 range ($C000..$C0FF).

2/7/2016
M.K.

Friday, February 5, 2016

Update on MKHBC-8-R2 6502 computer - prototyping banked RAM finished.

I recently made some changes (mostly corrections) to my design as I reported in my last blog update. Since then I did few more changes, in order to accommodate for the prototype of banked RAM I was making. I will explain in detail later on, but let me first introduce my banked RAM concept.

My banked RAM design is a simple solution with 128kB of static CMOS RAM chip WS628128LLPG-70, with address lines A14-A16 driven by 3 output ports (Q0, Q1 and Q2) of latch register 74LS374 to select 8 banks, 16 kB each.
That creates a simple output only port, which is at address $C000. Writing a value to that address, with older 5 bits masked out (zeroed) allows to select a bank # (0-7) of RAM in the range $8000-$BFFF.


This circuit will share a single board with my RTC circuit which also happens to be a battery sustained memory storage (some 200+ bytes available to store BIOS settings, short machine code or whatever). So it was logical IMO to put these two circuits on a single board (plus I save one bus slot by having one instead of 2 cards). 
Now, about the changes I made to accommodate banked RAM:
Note the /BRAM signal that drives the chip select pin of the memory chip. This pin was in the original design driven by a local address decoder made out of 2 NAND gates. The problem however was that my RTC circuit also requires one NAND gate. Therefore if I wanted to use just one 74LS00 chip on this board I'd be one NAND gate short. On the other hand If I added a 2-nd 74LS00, I'd have inefficient design with 3 unused NAND gates. But, the good news was that I also happened to have 2 unused NAND gates on my CPU board. I only had to do some design change to make the /BRAM address decoding circuit out of these free CPU board NAND gates and also a change the CPU bus design to be able to get that signal out of the CPU board into the CPU bus to the banked RAM board. That I did and it worked. I saved one 74LS00 chip. I changed the designation of the pin A30 on CPU bus from GND to /BRAM signal. Just a bit of soldering work. Saving one 74LS00 chip may not sound that important, but it kind of bothered me that I had 2 unused NAND gates on CPU board and would have another 3 on the RTC+BRAM board.



I didn't even have to write any special software to test it initially. I used monitor built into M.O.S. (OS running on my computer) to select memory bank, modify RAM contents and verify my changes stick. Then I switched memory bank and did the same, then switched back to the previously modified banks and checked again if my memory modifications were there. It all seems to work just fine. I only see one problem here. The banked memory resides in $8000-$BFFF range. The next address after this range is $C000 which happens to be the I/O 0 port - a memory bank selection port. Writing a value to that port selects the bank. However it is also sensitive to reading from this address in this design. Reading from that port automatically switches the bank to bank #0. You must be aware of this issue if you have a loop that at some point crosses the boundary $BFFF-$C000 and read from $Cxxx range is performed. This will switch your current memory bank, not a desired outcome in most cases I think. I learned this when my initialize memory command in my monitor program didn't produce the expected result. That was because I issued a command to initialize range $8000-$C000, which caused just one read from $C000 address at the end of the loop. Command was fine if I used range $8000-$BFFF, but then the last memory location: $BFFF wasn't initialized to the value I wanted. The same side effect happens when I dump memory and the range of dumped memory happens to cross $BFFF-$C000 boundary. I will have to think about some hardware correction to this problem or live with it. I think that signal /WE needs to be involved in the circuit that drives CLK input of the latch register. The data from data bus should only be latched on the register's output during write cycles.
I am thinking about putting signal /WE from CPU bus on one of the NAND inputs (U22), the one that now has both inputs driven by /IO0 signal would be multiplied with /WE so only if /IO0 and /WE are active, the data shall be latched at the outpout. I am going to try this and report the results in my next update.

This is all I've got today.

M.K.
2/5/2016

Wednesday, December 9, 2015

Update on MKHBC-8-R 6502 computer (now on revision #2) - prototyping RTC and banked RAM.

I had very productive 2 weeks or so with this project. 

To make long story short:

  • I assembled CPU and UART cards.
  • I discovered and mostly corrected errors in my design and technical problems in manufactured boards (bus contention, wrong address decoding scheme, bad solder joints/electrical contact problem on CPU bus, connector B2).
  • I built and tested RTC circuit prototype (based on DS1685 and Chris Ward's design of connecting this multiplexed bus type chip to 6502 bus).
  • I built and tested prototype of RAM bank switching register (based on 74LS374 latch register).

More details in my YouTube video: MKHBC-8-R2 - part 4.

My address decoding circuits design incorporated Phi2 signal in the decoding scheme. I consulted literature and expertise of knowledgeable people on the 6502 subject and decided this is not the correct approach. I removed the Phi2 signal from address decoders input. The Phi2 signal is now only used to synchronize reading/writing access cycles.


 

I tested two variants of RTC circuit:


 

 



and decided to go with 2-nd one (seems to be more reliable and less susceptible to problems resulting from long propagation times).

My I/O bus (data lines) was not properly separated from CPU bus resulting in bus contention and data corruption. Here is the corrected circuit:




I also have a concept of banked RAM:



The code testing the 74LS374 latch register alone (I just had LED-s connected to the data outputs):

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <peekpoke.h>
#include "mkhbcos_serialio.h"

char buf1[10], buf2[10];

void my_pause(uint16_t delay)
{
    int i = 0;

    for(i=0; i < delay; i++);
}

int main(void)
{
    int i = 0;
    unsigned char n = 0;
    char buf[5] = {0};

    POKE(0xC000,n);
    puts ("Hello! Testing port I/O #0 and latch 74LS374.\n\r");
    puts ("Press ENTER and observe the blinking lights...\n\r");

    gets(buf1);

    for (n=1, i=1; i<256; n++,i++) {
       buf1[0] = buf2[0] = 0;
       strcpy(buf1, itoa (i, buf2, 10));
       puts(buf1); puts("\r");
       POKE(0xC000,n);
       my_pause(2000);
    }

    for (i=0; i<10; i++) {
       POKE(0xC000,0);
       my_pause(2000);
       POKE(0xC000,0xFF);
       my_pause(2000);
    }
    POKE(0xC000,0);

    puts ("Test finished.\n\r");

    return 0;
}



Pictures of my prototype for your enjoyment (electronics hardware porn :-)) and some screenshots of serial command interface:





 

 


That would be all for today.

Thanks for visiting my blog.

12/9/2015
MK