Redirecting output to crude display device

Store Forums Video Experimenter General Discussion Redirecting output to crude display device

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #573
    UTAS
    Member

    My name is Hamish. I have been undergoing a summer research project for UTAS University, trying to use the VE board to send a video signal to some 3216 info board by Sure Electronics. The main chip in these info boards are HT3216C’s.

    At the moment I have created a 64×64 pixel display from 8 info boards. I can run these info boards no worries to display what I like with the Arduino.

    When I try to combine the LED code and the code from the ‘VideoFrameCapture’ VE example project, both fail to work. I can’t so much as light up a single LED between tv.capture() and tv.resume(). It seems like there is some inherent incompatibility between the different code because even having the initOverlay() method present breaks my LED code.

    Can anyone think of something perhaps to do with the interupts that might be causing the problem. I am using pins 10, 11, 12 and 13 on the Arduino Uno to drive the info boards.

    I made sure that the example project ‘VideoFrameCapture’ worked fine before trying to integrate it into my other code. I think I know roughly what I have to do to the VE code to send the data to the info boards for display, if only they didn’t interfer with each other.

    Any help with this would be very much appreciated.

    #1119
    Michael
    Keymaster

    Hi Hamish,

    First, I think there is a bug in VideoFrameCapture.pde that might affect you, so try changing the line:

    EICRA = _BV(ISC11);

    to

    EICRA = _BV(ISC01);

    Other questions: how are you driving the LED displays? That is, what software or libraries are you using? Does it use any interrupts or timers? If it uses timer1, then there would be a conflict with the VE software since TVout uses timer1. Feel free to post your code, surrounded by “Code” tags.

    #1118
    UTAS
    Member

    Firstly thanks for the speedy reply. The project I am working on is quite time sensitive. I tried your code change suggestion but no luck. It definately seems to be something about the initOverlay() method though cause I only have to comment that line out and my display lights up fine.

    I am very new to this so it is entirely possible I am making stupid mistakes. As far as I am aware there is no use of timers in my code. That said my code was borrowed and modified from others so I’m confident there are things I don’t know or understand about it. See code below.


    #include "HT1632C_1_4.h"

    TVout tv;
    unsigned char x,y;
    char s[32];

    void initOverlay()
    {
    TCCR1A = 0;
    // Enable timer1. ICES0 is set to 0 for falling edge detection on input capture pin.
    TCCR1B = _BV(CS10);

    // Enable input capture interrupt
    TIMSK1 |= _BV(ICIE1);

    // Enable external interrupt INT0 on pin 2 with falling edge.
    EIMSK = _BV(INT0);
    EICRA = _BV(ISC01);
    }

    void initInputProcessing()
    {
    // Analog Comparator setup
    ADCSRA &= ~_BV(ADEN); // disable ADC
    ADCSRB |= _BV(ACME); // enable ADC multiplexer
    ADMUX &= ~_BV(MUX0); // select A2 for use as AIN1 (negative voltage of comparator)
    ADMUX |= _BV(MUX1);
    ADMUX &= ~_BV(MUX2);
    ACSR &= ~_BV(ACIE); // disable analog comparator interrupts
    ACSR &= ~_BV(ACIC); // disable analog comparator input capture
    }

    ISR(INT0_vect) {//looks like it is an external interupt of sorts
    display.scanLine = 0;//seems to effect the sensitivity of the scan
    }

    //**************************************************************************************************
    //Function Name: OutputCLK_Pulse
    //Function Feature: enable CLK_74164 pin to output a clock pulse
    //Input Argument: displayNumber - which display to send to.
    //Output Argument: void
    //**************************************************************************************************
    void OutputCLK_Pulse(byte displayNumber)
    {
    switch(displayNumber){
    case 1:
    digitalWrite(clk1, HIGH);
    digitalWrite(clk1, LOW);
    break;
    case 2:
    digitalWrite(clk2, HIGH);
    digitalWrite(clk2, LOW);
    break;
    }
    }

    //**************************************************************************************************
    //Function Name: OutputA_74164
    //Function Feature: enable pin A of 74164 to output 0 or 1
    //Input Argument: x: if x=1, 74164 outputs high. If x?1, 74164 outputs low.
    //Output Argument: void
    //**************************************************************************************************
    void OutputA_74164(byte displayNumber, byte x)
    {
    switch(displayNumber)
    {
    case 1:
    if (x == 1)
    digitalWrite(cs1, HIGH);
    else
    digitalWrite(cs1, LOW);
    break;
    case 2:
    if (x == 1)
    digitalWrite(cs2, HIGH);
    else
    digitalWrite(cs2, LOW);
    break;
    }
    }


    //**************************************************************************************************
    //Function Name: Chip_Select
    //Function Feature: Enable the various HT1632C chips on the 3216 info boards.
    //Input Argument: select: HT1632C to be selected
    // If select = 0, select none.
    // If s < 0, select all.
    //**************************************************************************************************
    void Chip_Select(byte displayNumber, byte select)
    {
    byte i = 0;

    if (select < 0){ //Enable all HT1632Cs
    OutputA_74164(displayNumber, 0);
    for(i = 0; i < CHIP_MAX; i++)
    OutputCLK_Pulse(displayNumber);
    }
    else if(select == 0){ //Disable all HT1632Cs
    OutputA_74164(displayNumber, 1);
    for(i = 0; i < CHIP_MAX; i++)
    OutputCLK_Pulse(displayNumber);
    }
    else{
    OutputA_74164(displayNumber, 1);
    for(i = 0; i < CHIP_MAX; i++)
    OutputCLK_Pulse(displayNumber);

    OutputA_74164(displayNumber, 0);
    OutputCLK_Pulse(displayNumber);
    OutputA_74164(displayNumber, 1);
    for(i = 1; i < select; i++)
    OutputCLK_Pulse(displayNumber);
    }
    }

    //**************************************************************************************************
    //Function Name: Write_Bits
    //Function Feature: Write bits (up to 8) to h1632 on pins data, wrclk Chip is assumed to already be
    //chip-selected. Bits are shifted out from MSB to LSB, with the first bit sent being
    //(bits & firstbit), shifted till firsbit is zero.
    //Input Argument: displayNumber
    // : bits
    // : firstbit
    //**************************************************************************************************
    void Write_Bits (byte displayNumber, byte bits, byte firstbit)
    {
    switch(displayNumber){
    case 1:
    while(firstbit){
    digitalWrite(wrclk1, LOW);

    if (bits & firstbit)
    digitalWrite(data1, HIGH);
    else
    digitalWrite(data1, LOW);

    digitalWrite(wrclk1, HIGH);
    firstbit >>= 1;
    }
    break;
    case 2:
    while(firstbit){
    digitalWrite(wrclk2, LOW);
    if(bits & firstbit)
    digitalWrite(data2, HIGH);
    else
    digitalWrite(data2, LOW);

    digitalWrite(wrclk2, HIGH);
    firstbit >>= 1;
    }
    break;
    }
    }

    //**************************************************************************************************
    //Function Name: Send_Command
    //Function Feature: Send a command to the ht1632 chip.
    //Input Argument: displayNumber
    // : chipNo
    // : command
    //**************************************************************************************************
    static void Send_Command(byte displayNumber, byte chipNo, byte command)
    {
    Chip_Select(displayNumber, chipNo);
    Write_Bits(displayNumber, HT1632_ID_CMD, 0x04); //Send 3 bits of id: COMMMAND
    Write_Bits(displayNumber, command, 0x80); //Send the actual command
    Write_Bits(displayNumber, 0, 0x01); //One extra don't-care bit in commands.
    Chip_Select(displayNumber, 0);
    }


    //**************************************************************************************************
    //Function Name: Send_Data
    //Function Feature: send a nibble (4 bits) of data to a particular memory location of the ht1632.
    //The command has 3 bit ID, 7 bits of address, and 4 bits of data.
    //Select 1 0 1 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 D3 Free
    //Note that the address is sent MSB first, while the data is sent LSB first! This means that
    //somewhere a bit reversal will have to be done to get zero-based addressing of words and dots
    //within words.
    //Input Argument: displayNumber
    // : chipNo
    // : address
    // : data
    //**************************************************************************************************
    static void Send_Data(byte displayNumber, byte chipNo, byte address, byte data)
    {
    Chip_Select(displayNumber, chipNo);
    Write_Bits(displayNumber, HT1632_ID_WR, 0x04); // send ID: WRITE to RAM
    Write_Bits(displayNumber, address, 0x40); // Send address
    Write_Bits(displayNumber, data, 0x08); // send 4 bits of data
    Chip_Select(displayNumber, 0);
    }



    //**************************************************************************************************
    //Function Name: Get_Chip_Number
    //Function Feature: Takes a set of coordinates and returns the corresponding chip number.
    //Input Argument: x
    // : y
    //**************************************************************************************************
    byte Get_Chip_Number(byte x, byte y)
    {
    byte nChip;

    if(x >= 96)
    nChip = 7 + x / 16 + (y > 7 ? 2 : 0);
    else if(x >= 64)
    nChip = 5 + x / 16 + (y > 7 ? 2 : 0);
    else if(x >= 32)
    nChip = 3 + x / 16 + (y > 7 ? 2 : 0);
    else
    nChip = 1 + x / 16 + (y > 7 ? 2 : 0);

    return nChip;
    }

    //**************************************************************************************************
    //Function Name: xyToIndex
    //Function Feature: Get the value of (x, y)
    //Input Argument: x: X coordinate
    // y: Y coordinate
    //Output Argument: address of (x, y)
    //**************************************************************************************************
    byte xyToIndex(byte x, byte y)
    {
    byte addr;

    x = x % 16;
    y = y % 8;

    addr = (x << 1) + (y >> 2);

    return addr;
    }

    //**************************************************************************************************
    //Function Name: Calculate_Bit
    //Function Feature: calculate the bitval of y
    //Input Argument: y: Y coordinate
    //Output Argument: bitval
    //**************************************************************************************************
    byte Calculate_Bit(byte y)
    {
    return ( 8 >> (y & 3) );
    }


    //**************************************************************************************************
    //Function Name: Get_Pixel
    //Function Feature: get the value of (x, y)
    //Input Argument: x: X coordinate
    // y: Y coordinate
    //Output Argument: colour settled on (x,y) coordinates
    //**************************************************************************************************

    byte Get_Pixel(byte x, byte y)
    {
    byte addr, bitval, nChip;
    nChip = Get_Chip_Number(x, y);
    addr = xyToIndex(x, y);
    bitval = Calculate_Bit(y);

    if ( (shadowram[addr][nChip-1] & bitval) && (shadowram[addr+32][nChip-1] & bitval) )
    return ORANGE;
    else if (shadowram[addr][nChip-1] & bitval)
    return GREEN;
    else if (shadowram[addr+32][nChip-1] & bitval)
    return RED;
    else
    return 0;
    }

    //**************************************************************************************************
    //Function Name: Plot
    //Function Feature: Plot a pobyte on the display, with the upper left hand corner being (0, 0), and
    //the lower right hand corner being (X_MAX, Y_MAX).
    //The parameter "colour" could have one of the 4 values: black (off), red, green or yellow.
    //Input Argument: displayNumber:
    // x: X coordinate
    // y: Y coordinate
    // colour:
    //Output Argument: Colour set at the specified (x, y) coordinates.
    //**************************************************************************************************
    void Plot (byte displayNumber, byte x, byte y, byte colour)
    {
    byte nChip, addr, bitval;

    if (x < 0 || x > X_MAX || y < 0 || y > Y_MAX)
    return;
    if (colour != BLACK && colour != GREEN && colour != RED && colour != ORANGE)
    return;

    nChip = Get_Chip_Number (x, y);
    addr = xyToIndex (x, y);
    bitval = Calculate_Bit (y);

    switch(colour){
    case BLACK:
    if (Get_Pixel(x, y) != BLACK){
    // compare with memory to only set if pixel is other colour
    // clear the bit in both planes;
    shadowram[addr][nChip-1] &= ~bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    addr = addr + 32;
    shadowram[addr][nChip-1] &= ~bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    }
    break;
    case GREEN:
    if (Get_Pixel(x, y) != GREEN){
    // compare with memory to only set if pixel is other colour
    // set the bit in the green plane and clear the bit in the red plane;
    shadowram[addr][nChip-1] |= bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    addr = addr + 32;
    shadowram[addr][nChip-1] &= ~bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    }
    break;
    case RED:
    if (Get_Pixel(x, y) != RED){
    // compare with memory to only set if pixel is other color
    // clear the bit in green plane and set the bit in the red plane;
    shadowram[addr][nChip-1] &= ~bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    addr = addr + 32;
    shadowram[addr][nChip-1] |= bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    }
    break;
    case ORANGE:
    if (Get_Pixel(x, y) != ORANGE){
    // compare with memory to only set if pixel is other color
    // set the bit in both the green and red planes;
    shadowram[addr][nChip-1] |= bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    addr = addr + 32;
    shadowram[addr][nChip-1] |= bitval;
    Send_Data(displayNumber, nChip, addr, shadowram[addr][nChip-1]);
    }
    break;
    }
    }

    //**************************************************************************************************
    //Function Name: Plot
    //Function Feature: Plot a pobyte on the display, with the upper left hand corner being (0, 0), and
    //the lower right hand corner being (X_MAX, Y_MAX).
    //The parameter "colour" could have one of the 4 values: black (off), red, green or yellow.
    //Input Argument: displayNumber:
    // x: X coordinate
    // y: Y coordinate
    // colour:
    //Output Argument: Colour set at the specified (x, y) coordinates.
    //**************************************************************************************************
    void Paint (byte displayNumber, byte chipNo, byte addr, byte colour)
    {
    if(chipNo < 0 || chipNo > CHIP_MAX || addr < 0 || addr > 64)
    return;
    if(colour != BLACK && colour != GREEN && colour != RED && colour != ORANGE)
    return;

    Send_Data(displayNumber, chipNo, addr, 15);
    }

    //**************************************************************************************************
    //Function Name: Clear_Screen
    //Function Feature: Clear the display, and the shadow memory, and the snapshot memory. This uses
    //the "write multiple words" capability of the chipset by writing all 96 words of memory without
    //raising the chipselect signal.
    //**************************************************************************************************
    void Clear_Screen(byte displayNumber)
    {
    byte i, j, c;

    for(j = 1; j <= CHIP_MAX; j++){
    for(i = 0; i <= 96; i++)
    Send_Data(1, j, i, 0); // clear the display!
    }

    for(i = 0; i < 64; i++){
    for(j = 0; j < CHIP_MAX; j++)
    shadowram[j] = 0;
    }
    }

    void Test_Refresh_Rate(byte displayNumber)
    {
    int x, y;

    for (y = Y_MAX; y > -1; y--){
    for ( x = X_MAX; x > -1; x--){
    Plot(displayNumber, x, y, 1);
    //delay(4);
    }
    }
    //delay(1000);
    Clear_Screen(1);

    for (y = 0; y <= Y_MAX; y++){
    for ( x = 0; x <= X_MAX; x++){
    Plot(displayNumber, x, y, 2);
    }
    }
    Clear_Screen(displayNumber);
    for (y = 0; y <= Y_MAX; y++){
    for ( x = 0; x <= X_MAX; x++){
    Plot(displayNumber, x, y, 3);
    }
    }
    Clear_Screen(1);
    }

    void Test_Loop(byte displayNumber)
    {
    int x, y, c;
    for (c = 1; c < 4; c++){
    for (x = 0; x <= X_MAX; x++) {
    for (y = 0; y <= Y_MAX; y++) {
    Plot (displayNumber, x, y, c);
    //delay(2);
    }
    }
    }

    Clear_Screen (displayNumber);
    }

    void Test_Plot(byte displayNumber)
    {
    Plot(1, 15, 15, 2);
    Plot(1, 31, 15, 3);
    Plot(1, 47, 15, 2);
    Plot(1, 63, 15, 3);
    Plot(1, 79, 15, 2);
    Plot(1, 95, 15, 3);
    Plot(1, 111, 15, 2);
    Plot(1, 127, 15, 3);

    Plot(1, 15, 0, 1);
    Plot(1, 31, 0, 1);
    Plot(1, 47, 0, 1);
    Plot(1, 63, 0, 1);
    Plot(1, 79, 0, 1);
    Plot(1, 95, 0, 1);
    Plot(1, 111, 0, 1);
    Plot(1, 127, 0, 1);
    }

    void Test_Paint(byte displayNumber)
    {
    byte c, a, colour;

    for (colour = 1; colour < 4; colour++){
    for (c = 1; c <= CHIP_MAX; c++){
    for (a = 0; a < 64; a++){
    Paint (displayNumber, c, a, colour);
    }
    }
    Clear_Screen(displayNumber);
    }
    }

    void setup()
    {
    byte i, j;

    pinMode(cs1, OUTPUT);
    pinMode(clk1, OUTPUT);
    pinMode(wrclk1, OUTPUT);
    pinMode(data1, OUTPUT);

    for (j = 1; j <= CHIP_MAX; j++)
    {
    Send_Command(1, j, RC_MASTER_MODE);//Select on chip RC as the clocks master mode.
    Send_Command(1, j, N_MOS_COM_8);
    Send_Command(1, j, SYS_EN);//System on
    Send_Command(1, j, LED_ON);//LEDs on
    Send_Command(1, j, PWM_07);//7 is the sweet spot. Higher values produce more garbage on screen.

    //Clears the display quickly
    for (i = 0; i < 96; i++)
    {
    Send_Data(1, j, i, 0);
    }
    }

    tv.begin(PAL, W, H);
    //initOverlay();
    initInputProcessing();
    tv.fill(0);

    }

    void loop()
    {

    //tv.capture();
    //tv.fill(INVERT);
    //Test_Refresh_Rate (1);

    // tv.resume();
    // tv.delay_frame(5);

    //Test_Refresh_Rate (1);
    Test_Loop(1);
    //Test_Plot(1);
    //Test_Paint(1);
    }

    Inside of HT1632C_1_4.h there is nothing else included other than TVout.h and fontALL.h

    As it stands the code posted here paints a grid of pixels of different colours no worries. As soon as I remove the comment characters from initOverlay() nothing seems to work anymore.

    Once again your help with this is very much appreciated.

    #1050
    Michael
    Keymaster

    Where is the allocation of the shadowram array? How big is it? How much memory does your LED display code require? When using TVout, there’s very little memory left.

    #1051
    UTAS
    Member

    It is specified in the include as

    byte shadowram[64][(4 * Number_of_Displays)] = {0};

    So the size depends on the numbr of displays involved. I am currently using 4 displays, but I can get away with just 2 for this experiment. I never thought about the memory restriction. I am so used to programming for PC’s.

    Exactly how much memory would be left if I used the 128×96 dimensions specified in VideoFrameCapture?

    How hard do you think it will be to keep the two uses of the memory seperate?

    #1052
    Michael
    Keymaster

    ok, that’s a lot of memory. 128×96 resolution requires 1536 bytes, and there are only 2K on the Arduino. You really only have about 300 bytes to use for the rest of your program (you need to save some for stack). So the display library isn’t really going to work well, unless maybe you use 1 display. You can’t keep the memories separate — there’s just 2K of SRAM in the ATmega328 processor. You might try lower screen resolution, but I haven’t tested the VE image capture with different resolutions. Keep experimenting, and you might get something to work.

    #1053
    UTAS
    Member

    Thank you so much Michael. It was entirely issues with the limited memory. By using a res of 128*32 and only trying to display the picture on two info boards it works just fine now.

    #1054
    Michael
    Keymaster

    That’s great to hear! I hope you blog about your project and show it off to the world….!

Viewing 8 posts - 1 through 8 (of 8 total)
  • You must be logged in to reply to this topic.