Close

Main code for ATMEGA328PB

A project log for Remote control and image retrieval from Nikon DSLR

This post explains how to control a Nikon D3100 DSLR, and then get the images off of it.

sciencedude1990sciencedude1990 12/05/2020 at 01:470 Comments

Here is the main code for the ATMEGA328PB.  Note that this is for a xplained board.

The main idea of the code is to allow read and write of the MCU registers over the USART port (which for the xplained board, is a virtual COM port that is accessible over USB).  So, you set the serial port program to 9600 baud, 8 bits, no parity, and then to write the PORTB register, you type in something like w37=1  or w37=3  (i.e., decimal 37 is 0x25).  Please see the latest ATMEGA328PB datasheet, page 445 for the register map.

#include <avr/io.h>

// For the ATMEGA328PB xplained, 16 MHz is available on the external clock
// So, in the fuses section, LOW.CKDIV8 is unchecked
// LOW.SUT_CKSEL is set to Ext. Clock; Start-up time PWRDWN/RESET: 6 CK/14 CK + 65 ms
#define F_CPU 16000000UL
#include <util/delay.h>

#include <avr/interrupt.h>
#include "main.h"

// Place to store a string for USART communication
static char temp_string[64];
// Simple routine to output a serial string to the UART
void print_serial(const char * const print_string) {
    uint8_t temp = 0;
    
    while (temp < strlen(print_string)) {
        if ((UCSR0A & 0x20) == 0) {
            // Do nothing
            _delay_us(10);
        }
        else {
            // Send a byte
            UDR0 = (uint8_t) print_string[temp];
            temp = temp + 1;
        }
    }
    
    // Send ASCII 13 and 10
    temp = 0;
    while (temp < 2) {
        if ((UCSR0A & 0x20) == 0) {
            // Do nothing
            _delay_us(10);
        }
        else {
            if (temp == 0) {
                UDR0 = 13;
            }
            
            if (temp == 1) {
                UDR0 = 10;
            }
            temp = temp + 1;
        }
    }
}

// Place to store the user input
static uint8_t rx_in_buffer[64];
// The count of user input
static uint8_t rx_in_buffer_count = 0;

ISR(USART0_RX_vect) {

    // Read the character from the buffer
    uint8_t temp = UDR0;
    
    // Put the character in the buffer
    rx_in_buffer[rx_in_buffer_count] = temp;
    // Increment the count
    rx_in_buffer_count = rx_in_buffer_count + 1;
    // Count has to be less than 63
    if (rx_in_buffer_count > 63) {
        rx_in_buffer_count = 63;
    }

    // If the character is carriage return, process the command
    if (temp == 13) {
        go_process_command();
    }
}

void go_process_command() {
    // Print some characters in the buffer
    //snprintf(temp_string, 64, "Rx: %d, %d, %d, %d, %d, %d", rx_in_buffer[0], rx_in_buffer[1], rx_in_buffer[2], rx_in_buffer[3], rx_in_buffer[4], rx_in_buffer[5]);
    //print_serial(temp_string);
    
    // The read command
    if ((rx_in_buffer[0] == 114) || (rx_in_buffer[0] == 82)) { // The read command, r or R, expect decimal, for example, expect r72 to read 0x48, OCR0B
        //print_serial("r/R");
        
        // The location of the carriage return
        uint8_t ind_13 = 0;
        
        // Find character 13
        for (uint8_t ii = 1; ii < 64; ii++) {
            if (rx_in_buffer[ii] == 13) {
                ind_13 = ii;
                break;
            }
        }
        // Where character 13 was found
        //snprintf(temp_string, 64, "Char13: %d", ind_13);
        //print_serial(temp_string);
        
        // For processing the register address (convert ASCII characters to addr)
        uint8_t found_it = 0;
        uint16_t addr = 0;
        uint16_t pow_10 = 1;
        
        // If ind_13 was found, build up addr
        if (ind_13 > 1) {
            for (uint8_t ii = (ind_13 - 1); ii > 0; ii--) {
                if ((rx_in_buffer[ii] >= 48) || (rx_in_buffer[ii] <= 57)) {
                    addr = addr + (rx_in_buffer[ii] - 48) * pow_10;
                    pow_10 = pow_10 * 10;
                    found_it = 1;
                }
                else { // Unexpected character
                    found_it = 0;
                    break;
                }
            }
        }
        // Print the address and whether the parsing was as expected
        //snprintf(temp_string, 64, "addr: %u, found_it: %d", addr, found_it);
        //print_serial(temp_string);
        
        // Print the register value
        if (found_it == 1) {
            volatile uint8_t * reg_addr = (uint8_t *) addr;
            uint8_t temp_value = *reg_addr;
            snprintf(temp_string, 64, "Reg: %u (%#x) = %d (%#x)", addr, addr, temp_value, temp_value);
            print_serial(temp_string);
        }
    }
    else if ((rx_in_buffer[0] == 119) || (rx_in_buffer[0] == 87)) { // The write command, w or W, for example, expect w72=4
        //print_serial("w/W");
        
        // The location of the carriage return
        uint8_t ind_13 = 0;
        // Find character 13, carriage return
        for (uint8_t ii = 1; ii < 64; ii++) {
            if (rx_in_buffer[ii] == 13) {
                ind_13 = ii;
                break;
            }
        }
        
        // The location of the =
        uint8_t ind_eq = 0;
        for (uint8_t ii = 1; ii < 64; ii++) {
            if (rx_in_buffer[ii] == 61) {
                ind_eq = ii;
                break;
            }
        }
        
        // Where character = and 13 were found
        //snprintf(temp_string, 64, "Char=: %d, 13: %d", ind_eq, ind_13);
        //print_serial(temp_string);
        
        if ((ind_eq > 1) && ((ind_eq + 1) < ind_13)) {
            // For processing the register address (convert ASCII characters to addr)
            uint8_t found_it = 0;
            uint16_t addr = 0;
            uint16_t pow_10 = 1;
            // Get the register address
            for (uint8_t ii = (ind_eq - 1); ii > 0; ii--) {
                if ((rx_in_buffer[ii] >= 48) || (rx_in_buffer[ii] <= 57)) {
                    addr = addr + (rx_in_buffer[ii] - 48) * pow_10;
                    pow_10 = pow_10 * 10;
                    found_it = 1;
                }
                else { // Unexpected character
                    found_it = 0;
                    break;
                }
            }
            
            // Print the address and whether the parsing was as expected
            //snprintf(temp_string, 64, "addr: %u, found_it: %d", addr, found_it);
            //print_serial(temp_string);
            
            // Attempt to set the register value
            if (found_it == 1) {
                // Print the current value of the register
                volatile uint8_t * reg_addr = (uint8_t *) addr;
                //snprintf(temp_string, 64, "Reg: %u = %d, old value", addr, *reg_addr);
                //print_serial(temp_string);
                
                // Now, parse the value
                found_it = 0;
                pow_10 = 1;
                uint16_t in_val = 0;
                // Get the value
                for (uint8_t ii = (ind_13 - 1); ii > ind_eq; ii--) {
                    if ((rx_in_buffer[ii] >= 48) || (rx_in_buffer[ii] <= 57)) {
                        in_val = in_val + (rx_in_buffer[ii] - 48) * pow_10;
                        pow_10 = pow_10 * 10;
                        found_it = 1;
                    }
                    else { // Unexpected character
                        found_it = 0;
                        break;
                    }
                }
                
                // Print the value and whether the parsing was as expected
                //snprintf(temp_string, 64, "New value: %d, found_it: %d", in_val, found_it);
                //print_serial(temp_string);
                
                if ((found_it == 1) && (in_val <= 255)) {
                    // Write the value
                    *reg_addr = (uint8_t) in_val;
                    
                    snprintf(temp_string, 64, "Reg: %u (%#x) = %d, new value", addr, addr, in_val);
                    print_serial(temp_string);
                    
                }
            }
        }
    }
    else {
        print_serial("Command error");
    }
    
    // Last things to do
    // Set the buffer count to 0
    rx_in_buffer_count = 0;
    // Clear the buffer
    for (uint8_t ii = 0; ii < 64; ii++) {
        rx_in_buffer[ii] = 0;
    }
}

int main(void)
{    
    // Get the UART ready
    UBRR0H = 0; // Oscillator is 16 MHz, 9600 is decimal 103
    UBRR0L = 103;
    // Enable Rx interrupt, enable Rx, enable Tx
    UCSR0B = (1 << 7) | (1 << 4) | (1 << 3); // Enable RXCIE1, Enable Tx and Rx
    UCSR0C = (0 << 6) | (0 << 4) | (0 << 3) | (3 << 1); // Asynchronous, No parity, 8 bits
    _delay_ms(1);
    
    // Enable interrupts
    SREG |= (1 << 7);
    
    // PB5 is the USER_LED, labeled D200 on the ATMEGA328PB xplained board
    // PB0 and PB1 for the shutter control
    DDRB = (1 << 0) + (1 << 1) + (1 << 5);
    PORTB = 0;
        
    // Blink the LED waiting for serial port input
    print_serial("Go time!");
    while (1)
    {
        //PORTB &= 255 - (1 << 5);
        //_delay_ms(500);
        //PORTB |= (1 << 5);
        //_delay_ms(500);
        _delay_ms(1);        
    }
}

Discussions