Skip to content

6. Electronics design

Here’s this week’s assignment:

group project: use the test equipment in your lab to observe the operation of a microcontroller circuit board

individual project: redraw an echo hello-world board, add (at least) a button and LED (with current-limiting resistor) check the design rules, make it, and test it extra credit: simulate its operation

Files:

Drone controller:

drone controller attempt 2.brd

drone controller attempt 2.sch

RF remote device:

RF USART RECEIVER REV.3 actually manufacturing.brd

RF USART RECEIVER REV.3 actually manufacturing.sch

Drone 3D model:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Tutorials I made for this week:

On designing in Eagle PCB:

Group Project : OSCILLOSCOPE

I put together this little presentation on the basics of the oscilloscope.

This slide shows how the voltmeter takes voltage measurements at discrete moments in time of a capacitor charging up:

This slide shows how the voltmeter takes continuous voltage measurements of a capacitor charging up in time:

This shows the X and Y axes of the oscilloscope and how to change their scale.

This slide shows the probe and how it has two connectors, one to ground and the other to the signal in question.

Group Project : USART

I wanted to observe the sending of ASCII characters over USART connection.

Don’t forget to plug Rx to Tx and Tx to Rx! This is the only protocol I know of where this reversal is important.

I am using the Attiny2313 because it is, as far as I know, the only chip in the Attiny family that supports USART in hardware.

I first did this with a breadboard setup:

Here is the pinout of the USART cable:

And here is the capture of the signals being sent from the microchip to the computer :

Here is the code I’m running (Elliot Williams’ Make: AVR Programming was helpful for this code !) :

/*
 * ATTINY 2313 USART.c
 *
 * 
 * Author : FablabDigiscope

USB - FTDI 5V:
Rx to Tx
Tx to Rx
Gnd to Gnd

(NO LED ON chip's RX, but the one on TX doesn't seem to interfere)

To view your Serial output in AVR Studio, 
1. Click Tools > Data Visualizer
2. Under Configuration>Modules, click External Connection.
3. Double click Serial Port
4. Configure your port. e.g. Set the Baud Rate to 9600 
5. Select your COM port
6. Click Connect.

appears to send ASCII from AVR Studio terminal

 */ 

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

#define BAUD 9600                                   // define baud
#define BAUDRATE ((F_CPU)/(BAUD*16UL)-1)            // set baud rate value for UBRR. I copied the UL and don't understand it!

// function to initialize UART
void uart_init (void)
{
    UBRRH = (BAUDRATE>>8);                      // shift the register right by 8 bits
    UBRRL = BAUDRATE;                           // set baud rate
    UCSRB|= (1<<TXEN)|(1<<RXEN);                // enable receiver and transmitter
    UCSRC|= (1<<UCSZ0)|(1<<UCSZ1);             // 8bit data format + asynchronous mode
}

// function to send data
void uart_transmit (unsigned char data)
{
    while (!(UCSRA & (1<<UDRE)));                // wait while register is free
    UDR = data;                                   // load data in the register
}

unsigned char uart_recieve (void)
{
    while(!(UCSRA) & (1<<RXC));           // wait while data is being received
    return UDR;                             // return 8-bit data
}

int main(void)
{

DDRA = 0b00000001;
uart_init();

unsigned char a;

    while (1) 
    {
        //transmit
//  uart_transmit('alpha');
//  PORTA = 0b00000001;
//  _delay_ms(1000);
//  PORTA = 0b00000000;
//  _delay_ms(1000);

        //recieve
    a = uart_recieve(); 
    _delay_ms(100);


    if (a == 'j') //corresponds to a 'j' for instance, but not a 'c'
        {
            PORTA = 0b00000001;
            _delay_ms(1000);
            PORTA = 0b00000000;
            _delay_ms(1000);
            PORTA = 0b00000001;
            _delay_ms(1000);
            PORTA = 0b00000000;
            _delay_ms(1000);    //some kind of local echo thing makes it repeat?? Or else the register is not being cleared
        }
    }
}

Here is a sheet I made to remind me how setting bits with masks works:

Group Project : DIGITAL READ

Here I just wanted to observe threshold at which an analog read goes from low to high.

Here I am using the Attiny84 along with a 10K potentiometer. The two ends of the potentiometer are attached to GND and VCC and the middle pin is going to PA1 (ADC1). I have PA0, the ADC reference voltage pin I selected, connected to 5V. I have an LED on PB1 to display the treshold being crossed. I am running the microchip at 8MHz.

Here is the test setup:

Here is the code I’ve flashed :

/*
 * Attiny84 analog read.c
 *
 * 
 * Author : marrs
 */
#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>


int main(void)
{
    ADMUX |= (1 << REFS0); // PA0 ref voltage
    ADMUX = (0b11110000 & ADMUX) | PA1; // PA1 input select
    ADCSRA |= (1 << ADPS2) | (1 << ADPS0); // set clock to 32 divisions for 8MHz
    ADCSRA |= (1 << ADEN); /* enable ADC */

    uint16_t adcValue; //16 bit variable because the ADC on the Attiny84 is 10 bits.
    DDRB = 0b00000001; //LED on PB0

    while (1)
    {
    ADCSRA |= (1 << ADSC); /* start conversion */
    _delay_ms(1); //instead of waiting for ready flag
    adcValue = ADC; /* store high byte into adcValue */

    if (adcValue > 200)
    {
        PORTB = 0b00000001;
    }
    else
    {
        PORTB = 0b00000000;
    }
    _delay_ms(50);
}
return(0);
}

The only tricky part here for me (being a C newb!) was storing just the high byte of the ADC and understanding the size of data types relative to what is being stored inside of them. I couldn’t manage to read just ADCH or ADCL.

Elliot Williams’ Make: AVR Programming came in handy for this project.

The result (the analog value from the potentiometer is in yellow, the green LED which is triggered is in green):

Group Project : DEBOUNCING

Though this does not involve investigating the operation of microcontroller, it does involve the workings of a mechanism which the microcontroller listens to: a button! It also involves the use of the oscilloscope, which is the main goal of the group assignment this week.

Microchips exist at a different time scale from humans, their electronics “hearts” can beat millions of times a second – think of them as extremely hyperactive brains. Microchip pins can either “listen” (take input) or send voltage signals (output). When in input mode, a microchip pin can detect the state of a button (button pushed, or button unpushed) and in output mode it can turn on or off an LED. When a human pushes down on a mechanical button and lifts their finger away, the spring inside the button pushes the button back up to its initial position. During this process there may be micro-bounces of the metal contacts inside the button. A microchip listening to the input at this time can interpret these micro bounces as changes of button states, and if the number of these bounces is odd, the microchip can conclude that the button has settled into a state other than the one that it is from the perspective of a human being.

To resolve this issue, known as bouncing, there are two approaches: software or hardware. In software, the microchip can effectively check the state of the button twice, after a delay which exeeds the period of time that a button can bounce for. This code essentially detects a change in button state, then waits a few milliseconds and checks to see that the button is in the same state. This effectively rules out the possibility of having detected a button debounce. The second possibility is to do the debouncing with hardware, that is, electronically. In this approach, we stop the erroneous button state signals from arriving at the microchip in the first place. This typically involves using a small capacitor, the hardware equivalent of a delay in our code.

Here is the proper way to pull a button down with a pull-down resistor so that the micro controller’s input is not floating before the button is pushed and connected to VCC:

I have attached a probe to the button on the micro controller side and I am pushing on the button with the oscilloscope is One Shot Mode with the trigger set to change in voltage.

Just to note, this is how the button is connected :

We want the microchip to read two distinct states: HIGH or LOW. When the button is unpressed, the 10K pull-down resistor connects PB0 to GND. When the button is pressed, the pin is connected to 5V directly, a more appealing option for the electrons than the large resistor towards ground. This circuit is NOT debounced.

Behold, the bounce galery :

As you can see there is quite a lot of bouncing going on here. The solution in software is to read the button state, then if it is high, to wait a few a millisecond or so and test again to see if it’s still in the same state.

Here is my debounce code :

int main(void)
 {
     DDRD  = 0b11111111; // Port D output
     DDRB  = 0b00000000; // Port B input

PORTB = 0b11111111; // pull-up resistors enabled

while (1) 
{

    if (PINB & (1<<0))
    {
        _delay_ms(50); //

                if (PINB & (1<<0))
                {

                    PORTD = 0b00000000; // turn off LED
                }
    }        


    else 
    {
         PORTD = 0b11111111; // turn on LED
    }
}
return(1);
}

Individual Project : REDRAW A BOARD AND ADD AT LEAST A BUTTON + LED

Here I redrew a board I designed for my Egypt Fab Conference Workshop which never saw the light of day. I added two joystick buttons, and the controller board communicates with a drone which has motors and an LED. It uses an Atmega328 instead of an Attiny but the principles are the same.

This video shows the eagle work, the board engraving, and the soldering under a microscope. Enjoy !

As for design rules, I am using 0.4mm thick traces on a single sided FR4 board and cutting with the Fiber laser as described in week 4.

The controller works with this drone I designed recently :

And with the circuit on top:

Here are the images I engraved for the controller:

…and for the drone:

And here is the final product:

Radios are working, I’m struggling with the best way to poll the joysticks. I think I need to use Pin Change Interrupts (Interrupts for pins other than those dedicated for interrupting). The other option is just to have polling:

if (UP == HIGH){ Send(UP);}

if (DOWN == HIGH){ Send(DOWN);}

etc.

*Later realized that I messed this up, what I want is to check if the pin is LOW (i.e. if it is connected to ground and not internally pulled up) and then have an else at the end which turns to HIGH.

*I forgot to put pull-up resistors on the actual board so I used the avr’s internal ones. In arduino you do this like so:

pinMode(A0, INPUT_PULLUP);

I reprinted the drone frame with .1mm extra space around the circle and now the motors fit snuggly.

Dissapointed that the joysticks only register an up or down when also pushed down…

Here is the joystick sending code:

********************************************************************************

// RFM69HCW Example Sketch
// Send serial input characters from one RFM69 node to another
// Based on RFM69 library sample code by Felix Rusu
// http://LowPowerLab.com/contact
// Modified for RFM69HCW by Mike Grusin, 4/16

// This sketch will show you the basics of using an
// RFM69HCW radio module. SparkFun’s part numbers are:
// 915MHz: https://www.sparkfun.com/products/12775
// 434MHz: https://www.sparkfun.com/products/12823

// See the hook-up guide for wiring instructions:
// https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide

// Uses the RFM69 library by Felix Rusu, LowPowerLab.com
// Original library: https://www.github.com/lowpowerlab/rfm69
// SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout

// Include the RFM69 and SPI libraries:

#include <RFM69.h>
#include <SPI.h>

// Addresses for this node. CHANGE THESE FOR EACH NODE!

#define NETWORKID 0 // Must be the same for all nodes (0 to 255)
#define MYNODEID 1 // My node ID (0 to 255)
#define TONODEID 2 // Destination node ID (0 to 254, 255 = broadcast)

// RFM69 frequency, uncomment the frequency of your module:

//#define FREQUENCY RF69_433MHZ
#define FREQUENCY RF69_915MHZ

// AES encryption (or not):

#define ENCRYPT true // Set to “true” to use encryption
#define ENCRYPTKEY “TOPSECRETPASSWRD” // Use the same 16-byte key on all nodes

// Use ACKnowledge when sending messages (or not):

#define USEACK true // Request ACKs or not

// joystick and leds

#define LED1 A0 // LED positive pin
#define UP1 A2//
#define LEFT1 A3//
#define RIGHT1 A4//
#define DOWN1 A5//

#define LED2 A1 // LED positive pin
#define UP2 8//
#define LEFT2 9//
#define RIGHT2 7//
#define DOWN2 6//

// Create a library object for our RFM69HCW module:
RFM69 radio;

void setup()
{
// leds and joystick init

pinMode(LED1,OUTPUT);
pinMode(LED2,OUTPUT);

pinMode(UP1,INPUT_PULLUP);
pinMode(LEFT1,INPUT_PULLUP);
pinMode(RIGHT1,INPUT_PULLUP);
pinMode(DOWN1,INPUT_PULLUP);

pinMode(UP2,INPUT_PULLUP);
pinMode(LEFT2,INPUT_PULLUP);
pinMode(RIGHT2,INPUT_PULLUP);
pinMode(DOWN2,INPUT_PULLUP);

// Initialize the RFM69HCW:

radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
radio.setHighPower(); // Always use this for RFM69HCW

// Turn on encryption if desired:

if (ENCRYPT)
radio.encrypt(ENCRYPTKEY);
}

void loop()
{
//setting up variables for button states

int sensorVal0 = digitalRead(UP1);
int sensorVal1 = digitalRead(LEFT1);
int sensorVal2 = digitalRead(RIGHT1);
int sensorVal3 = digitalRead(DOWN1);
int sensorVal4 = digitalRead(UP2);
int sensorVal5 = digitalRead(LEFT2);
int sensorVal6 = digitalRead(RIGHT2);
int sensorVal7 = digitalRead(DOWN2);

// SENDING

// In this section, we’ll gather serial characters and
// send them to the other node if we (1) get a carriage return,
// or (2) the buffer is full (61 characters).

// If there is any serial input, add it to the buffer:

char buffer[5] = “z”;

//BUTTON READING AND SENDING

if (sensorVal0 == LOW) {
buffer[0] = ‘a’;
digitalWrite(LED2, HIGH);
}

else if (sensorVal1 == LOW) {
buffer[0] = ‘b’;
digitalWrite(LED2, HIGH);
}

else if (sensorVal2 == LOW) {
buffer[0] = ‘c’;
digitalWrite(LED2, HIGH);
}

else if (sensorVal3 == LOW) {
buffer[0] = ‘d’;
digitalWrite(LED2, HIGH);
}

else if (sensorVal4 == LOW) {
buffer[0] = ‘e’;
digitalWrite(LED1, HIGH);
}

else if (sensorVal5 == LOW) {
buffer[0] = ‘f’;
digitalWrite(LED1, HIGH);
}

else if (sensorVal6 == LOW) {
buffer[0] = ‘g’;
digitalWrite(LED1, HIGH);
}

else if (sensorVal7 == LOW) {
buffer[0] = ‘h’;
digitalWrite(LED1, HIGH);
}

else {
buffer[0] = ‘z’;
digitalWrite(LED2, LOW);
digitalWrite(LED1, LOW);
}

// END OF BUTTON STUFF

if(buffer[0]!=’z’)
{

static int sendlength = 3;

radio.sendWithRetry(TONODEID, buffer, sendlength);
}

}

********************************************************************************

 ```


And here is the receiver code:

 ```


*********************************************************************************

// RFM69HCW Example Sketch
// Send serial input characters from one RFM69 node to another
// Based on RFM69 library sample code by Felix Rusu
// http://LowPowerLab.com/contact
// Modified for RFM69HCW by Mike Grusin, 4/16

// This sketch will show you the basics of using an
// RFM69HCW radio module. SparkFun’s part numbers are:
// 915MHz: https://www.sparkfun.com/products/12775
// 434MHz: https://www.sparkfun.com/products/12823

// See the hook-up guide for wiring instructions:
// https://learn.sparkfun.com/tutorials/rfm69hcw-hookup-guide

// Uses the RFM69 library by Felix Rusu, LowPowerLab.com
// Original library: https://www.github.com/lowpowerlab/rfm69
// SparkFun repository: https://github.com/sparkfun/RFM69HCW_Breakout

// Include the RFM69 and SPI libraries:

#include <SPI.h>
#include <RFM69.h>

// Addresses for this node. CHANGE THESE FOR EACH NODE!

#define NETWORKID 0 // Must be the same for all nodes
#define MYNODEID 2 // My node ID
#define TONODEID 1 // Destination node ID

// RFM69 frequency, uncomment the frequency of your module:

//#define FREQUENCY RF69_433MHZ
#define FREQUENCY RF69_915MHZ

// AES encryption (or not):

#define ENCRYPT true // Set to “true” to use encryption
#define ENCRYPTKEY “TOPSECRETPASSWRD” // Use the same 16-byte key on all nodes

// Use ACKnowledge when sending messages (or not):

#define USEACK true // Request ACKs or not

// Packet sent/received indicator LED (optional):

#define LED 1 // LED positive pin

// Create a library object for our RFM69HCW module:

RFM69 radio;

void setup()
{
// Open a serial port so we can send keystrokes to the module:

pinMode(LED,OUTPUT);
digitalWrite(LED,LOW);

radio.initialize(FREQUENCY, MYNODEID, NETWORKID);
radio.setHighPower(); // Always use this for RFM69HCW

if (ENCRYPT)
radio.encrypt(ENCRYPTKEY);

}

void loop()
{

// RECEIVING

// In this section, we’ll check with the RFM69HCW to see
// if it has received any packets:

if (radio.receiveDone()) // Got one!
{
// Print out the information:

// The actual message is contained in the DATA array,
// and is DATALEN bytes in size:

// char message = radio.DATA[0];

// RSSI is the “Receive Signal Strength Indicator”,
// smaller numbers mean higher power.

// Send an ACK if requested.
// (You don’t need this code if you’re not using ACKs.)

if (radio.ACKRequested())
{
radio.sendACK();
}
Blink(LED,10);
}

}

void Blink(byte PIN, int DELAY_MS)
// Blink an LED for a given number of ms
{
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}

*********************************************************************************

I used Arduino to generate the .hex code (Under Sketch I clicked Generate Compiled Binary and then Show Sketch Folder), and then uplodaded it with AVR Studio as I showed in the electronics production week.

I’ve got some old drones which I have taken apart for batteries, motors and propellers. The motors are slightly larger to I have redesigned the 3D print. The motors are 8.5mm in diameter and I found that subtracting with an 8.8mm cylinder make for the snuggest fit.


Last update: June 18, 2021