Arief Kurniawan
Arief Kurniawan

Reputation: 43

Displaying text on an LCD Matrix with SPI communication responsively using ATmega328P

Background and objective

I am working on a simple AVR C project with an ultrasonic sensor (HC-SR04) and SPI communication between the ATmega328P (Arduino) and three cascaded MAX7219 LCD matrix. The objective of the system is to detect and measure the distance using the ultrasonic sensor and display an appropriate message on the LCD matrix simultaneously.

What's working

I can perform the main functionality of both components separately. I can detect and measure distance with the HC-SR04 and I can display the text on the LCD matrix and scroll the text as well. The HC-SR04 repeatedly measures the distance roughly every 5ms.

Problem

The problem rises when I want to perform both functionalities simultaneously. What happens is that the text message is displayed repeatedly on the LCD matrix but not in full. I understand this is because I repeatedly measure the distance using the ultrasonic sensor. My guess would be interrupts, however I am not sure how to tackle this issue.

Is there a way of displaying the "last measured distance" fully on the LCD whilst still measuring distance?

The code (main parts):

// GLOBALS
volatile unsigned short echo_time;
volatile unsigned short distance;

// Text to display to LCD
char text[25] = "DEFAULT";

// Fire sensor
void fire_sensor()
{
    // Fire the trigger pin
    PORTD |= (1 << TRIG_PIN);
    
    // Wait for 10 microseconds in reference to the data sheet
    _delay_us(10);
    
    // Toggle the trigger pin to turn off
    PORTD &= ~(1 << TRIG_PIN);
}

// Measure distance
void measure_distance()
{   
    // Wait until echo pin is low and timer counter has changed
    while (echo_time == 0);
    
    // Calculate the distance in centimeters
    distance = echo_time/58;
    
    if (distance <= 60)
    {   
        strcpy(text,"VEHICLE DETECTED");
        PORTB = 0b00000001; // Turn DEBUG LED ON
        
    }
    else
    {
        strcpy(text,"DETECTED NOTHING");
        PORTB = 0b00000000; // Turn DEBUG LED OFF
    }
}

// *************************** MAIN *************************** //

int main(void)
{
    // Enable interrupts
    sei();
    
    // Initialise components
    EchoSetup();
    SPISetup();
    
    InitMatrix();
    InitBuffer();
    ClearMatrix();
    
    // Get length of text
    size_t length = strlen(text);
    
    /* Replace with your application code */
    while (1) 
    {
        fire_sensor();
        measure_distance();
        _delay_ms(5);
        
        // Display to LCD matrix character by character
        for (uint16_t i = 0; i < length; i++)
        {
            PushCharacter(text[i] - 32);
            PushBuffer(0x00);                       // Add empty column after character for letter spacing
        }
        
    }
}

// *************************** INTERRUPTS *************************** //

ISR(INT0_vect)
{
    // ECHO is high (signal has come back) stop counter
    if(PIND &(1 << PIND3)){
        
        // Reset the counter
        TCNT1 = 0;
    }
    else
    {
        // ECHO pin is low and the counter starts
        echo_time = TCNT1;
    }
}

Upvotes: 1

Views: 206

Answers (1)

Rev
Rev

Reputation: 6092

I would suggest to make measure_distance() non-blocking.

// Measure distance 
// (returns true if distance was process and text was updated)
bool process_distance()
{   
    if (echo_time == 0)
        return false;
    
    // Calculate the distance in centimeters
    distance = echo_time/58;
    
    if (distance <= 60)
    {   
        strcpy(text,"VEHICLE DETECTED");
        PORTB = 0b00000001; // Turn DEBUG LED ON
        
    }
    else
    {
        strcpy(text,"DETECTED NOTHING");
        PORTB = 0b00000000; // Turn DEBUG LED OFF
    }

    echo_time = 0;  // reset echo time
    return true;
}

In your while loop, you no longer wait for the result to be ready but repeatedly check if it "was" ready and display the result.

while (1) 
{
    bool refreshDisplay = false;
    fire_sensor();
    refreshDisplay = process_distance();
    _delay_ms(5);  // Why this delay?
    
    // Only refresh if necessary
    if (refreshDisplay)
        // Display to LCD matrix character by character
        for (uint16_t i = 0; i < length; i++)
        {
            PushCharacter(text[i] - 32);
            PushBuffer(0x00);                       // Add empty column after character for letter spacing
        }
    }
}

This is still not "clean code" because some responsibilities are loosely coupled between functions. But improving code like this comes with experience.

Upvotes: 1

Related Questions