Multiplexing seven segment display on AVR Atmega324A - c

I've been stuck on this all day, I'm trying to create a count down timer using two seven segment displays. I want it to start at 20 and count down to zero. While 10< I only want to have the left display on(i.e no 0 in the tens place). I'm using an Atmega 324A. I have all of port C connected to the display segments and am using PIND0 to toggle between the two. Here is what I have so far.
#include <avr/io.h>
#include<util/delay.h>
int main(void) {
int prescale = (8000000/8)/1000-1;
int digit = 1;
uint8_t display;
int seven_seg = {0x3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0C07,0X7F,0X6F};
// Set OC1 to output
DDRD = (1<<0);
DDRC = 0xFF;
OCR1A = prescale;
//clear counter on compare match
TCCR1A = (0<<COM1A1) | (1<<COM1A0);
//Set Prescale and CTC Mode
TCCR1B = (0<<CS12) | (1<<CS11) | (0<<CS10) | (0<<WGM13) | (1<<WGM12);
while(1) {
display++;
if(display>50) display = 0;
for (i = 250; i>0; i--){
PORTD ^= 0<<PIND0;
PORTC = seven_seg[display%10];
PORTD ^= 1<<PIND0;
_delay_ms(100);
for (i = 250; i>0; i--){
PORTD ^= 1<<PIND0;
PORTC = seven_seg[display/10];
PORTD ^= 0<<PIND0;
_delay_ms(100);
}
}
while((TIFR1 & (1<<OCF1A)) == 0) {}
TIFR1 &= (1 << OCF1A);
}
}
All this does is set both displays to 0. Do I need another for loop to iterate through the seven_seg[] array while it's doing this? really not sure how to tackle this one. Any help would be great.

you make 2 big faults:
you don't use timer
you should separate display-driving-logic from value-generating-logic
best thing would be you split the tasks and plan how to implement this.
Task one: providing the data to display
Task two: transfering that data to a display friendly representation
Task three: the aktual displaying of that data
Task one is Easy. lets assume you want to display integers and you have three 7-seg-disps.
So task one is to provide some Data to display.
int16_t numberToDisplay = 234;
Task two is also not that hard. a display friendly representation would be one byte per display element.
#define NUM_7SEGS 3
volatile uint8_t dispData[NUM_7SEGS]; // volatile since it is be accassed by different contexts
now we need some mechanism that transfers the input value to the display data
void val2DispData(int16 val)
{
uint8_t i;
for(i=NUM_7SEGS; i; --i){
uint8_t r = (uint8_t)(val%10);
val /= 10
dispData[i-1] = seven_seg[r];
}
}
fine and now?
Task three is the most difficult one. we need someone who says the output what to do.
Since the want to multiplex the 3 display elements that means:
deactivate the current display element
put the data of the next digit to the outport
activate the next display element
wait a bit.
and this 4 steps we want to do very fast so that the observer does not recognize that only one element is active at a time.
since this is totally independent of the other program logic, we need to do this in "background".
your main program flow simply calls that function and the background timer ISR worrries about displaying.
So we have to set up a timer and call the switching of element data in its interrupt service routine. (for setting up timer and timer interupts please refer another tutorial)
// this have to be called cyclic from timer isr
// frequency is not that important but should be at
// least NUM_7SEGS * 200 Hz to not look ugly
void cyclicDisplayTask()
{
static uint8_t currentElement = 0;
// disable all elements
PORTD = 0;
// put data on the port
PORTC = dispData[currentElement]; // this is why the volatile is necessary. without the compiler would not notice that values may be changed by the main program flow
// enable next element
PORTD = (1<<currentElement);
++currentElement;
if(currentElement>=NUM_7SEGS){
currentElement = 0;
}
}
of course you have to adapt the enabling of specific display elements to your hardware.
please also note that you may youse a transistor to drive the element. an AVR port pin is strong enogh to drive a single segment but the other side that drives the common anodes/cathodes of the segment may be overload. This of course depends on the leds within the segments. if this are low current leds (~2mA) it is ok.

Related

Using Timers for Blink & Count

I'm creating part of a program right now for a personal project. This is my first year ACTUALLY coding and have been studying for hours to understand many concepts so please be nice and try not to be rude as others have...
The project of mine is an AVRDUDE using a chip called ATMEGA328P in a program called Microchip studios.
[This project is having 3 LED count every time a switch is pressed, I should have them count continuously and change to the next number every second using a TIMER 1.
**counting in binary from 0-8 then rolling over **
I need some help on one aspect of it which is using interrupts after I have already created a blinking LED to use a TIMER0 instead of delays.
I have made my fourth LED flash at 5Hz which is the blinking part of my code include below at the end of this question.
Now the problem I am running into is trying to create interrupts for the 3 LEDS count every time a switch is pressed, I should instead have them continually count, changing to the next number approximately every second, using TIMER1.]
This is my code for the project
Again please be nice and at least lead me in some type of direction...
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 1000000UL
int global = 0 ;
volatile uint8_t overflow0; // Variable for counting overflows for first timer
volatile uint8_t overflow1; // Variable for counting overflows for second timer
void FirstTimer() // Initialize Timer 1
{
TCCR0B |= (1<<CS02) | (1<<CS00); // Prescaler of 1024
TCNT0 = 0;
}
void SecondTimer() //Initialize Timer 2
{
TCCR1B |= (1<<CS11) | (1<<CS10);
TCNT1 = 0;
}
int main(void)
{
DDRB |= (1 << DDB0);
FirstTimer(); // Calling timer 1 and 2 initialization
SecondTimer();
while (1)
{
{
if (TCNT0 >= 195) // Amount of ticks needed
{
PORTB ^= (1 << PORTB0); //LED on
TCNT0 = 0; //Reset counter
}
}
{
if (TCNT1 >= 15625) // Ticks needed
{
TCNT1 = 0; // Timer reset
}
}
}
}
An approach you could use is keeping a count variable (unsigned char - assuming 8 bit register width) and in your Timer ISR simply increment the count and write that value to the output register. Once count > 8 set to zero.
Configure the Timer mode to trigger on overflow w/ reset to zero.
Then you do some basic math using the clock speed and timer ceiling (overflow value) to calculate the frequency you want the overflow ISR to occur (increment the LED count values).
Note that for this to work the LEDs need to be on the first 3 pins.
i.e.
P1.0 = LED1
P1.1 = LED2
P1.2 = LED3
...
If not connected like this then you can still make it work with additional bit manipulation (shifts and masks).

I need to know why my interrupts are not working here

I am using stm8l - discovery and i have created a code for toggling a led for every 1 second using timer (TIM1) but this is not working properly. I am missing something here in my configuration
I could enter the interrupt function for the first time but after that it does not enter the interrupt function. Someone please look into and help me out
enter code here
#include <iostm8l.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "defs.h"
unsigned int count = 0;
#svlreg #interrupt void TIM1(void)
{
count += 1;
TIM1_SR1 &= ~(0x01);
}
main()
{
CLK_DIVR = 0x00; // Set the frequency to 16Mhz
CLK_PCKENR2 = 0x02; // clock for timer1
PC_DDR = 0x80; // direction output for led
PC_CR1 = 0x80; // fast push pull mode
PE_DDR = 0x80; // direction output for led
PE_CR1 = 0x80; // fast push pull mode
TIM1_PSCRH = 0x3e; //to create a frequency for 1000 hz
TIM1_PSCRL = 0x80; // so prescalar is 16000
TIM1_CR1 = 0x01;
TIM1_IER = 0x01;
_asm("rim\n");
while(1)
{
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}
}
}
The interrupt enters only one time but after that it does not enter. So variable "count" remains at value 1
You are using magic numbers for the bit masks instead of defined constants, so the code is pretty damn hard to read for you and me both. Change this so that the code ends up like for example
TIM1_SR1 &= ~TIM1_SR1_UIF;
Since this is a 8 bit MCU it is also absolutely essential that you u suffix all integer contants, or they will be of type signed int, which you don't want.
For example this code TIM1_SR1 &= ~(0x01); is equivalent to TIM1_SR1 &= -2. Very easy to write accidental subtle bugs this way. I recommend studying Implicit type promotion rules.
It is highly recommended to disassemble every ISR you write to see what machine code you actually end up with, and single step it through the debugger while watching the register as well. This particular register seems to ignore having 1 written to it, so you could probably just do TIM1_SR = TIM1_SR1_UIF;. Incorrectly cleared timer flags inside ISRs is one of the most common bugs in embedded systems.
Quoting the manual:
UIF: Update interrupt flag
– At overflow or underflow if UDIS = 0 in the TIM1_CR1 register
– When CNT is re-initialized by software using the UG bit in TIM1_EGR register, if URS = 0 and UDIS = 0 in the TIM1_CR1 register.
– When CNT is re-initialized by a trigger event (refer to the TIM1_SMCR register description), if URS = 0 and UDIS = 0 in the TIM1_CR1 register
Your code doesn't appear to do any of this, so it is pretty safe to assume the timer counter isn't reset.
Another problem is that count must be declared as volatile or otherwise the compiler might optimize out this code completely:
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}

Multiple LEDs and using and input button

I am really stuck on part a and b for problem 1 below. I am really confused on how to change the multiply and divide functions to change pins/LED’s, using the << and >> functions instead.
Any help would be much appreciated. Thanks!
Multiple LEDs and using and input button
Modify the C program:
a. Instead of using the multiply and divide functions to change pins/LED’s, use the << and >> functions. References: Deitel and Deitel “C, How to Program and https://en.wikipedia.org/wiki/Operators_in_C_and_C
b. Change the clock frequency in the program to 1 MHz and make the on/off time of each LED .1 seconds. This should make the rotation visibly faster. (Remember to change the _XTAL_FREQ value since this is used for the __delay_ms() function built into XC8)
Devices:
Low Pin Count board (16F1829 on board) and 44-Pin Demo Board are both on same backboard. (You only use the 16F1829 for this lab.)
PICKIT 3 programmer with USB cable
MPLAB X (I used v3.00 but a different version may be on lab computers))
Microchip XC8 C Compiler User Manual
PIC16F1829 Data Sheet
PICkit 3 User’s Guide
Low Pin Count Board User Guide
“C How to Program” Deitel, Pearson/Prentice-Hall (Any edition)
Internet Browser Search Engine for research (Google, Bing, etc)
upload_2018-9-5_23-27-22.png
The code is below.
/*
LEDs on for approximately 0.5 sec.
PIC: 16F1829 Enhanced Mid-Level
Compiler: XC8 v1.34
IDE: MPLABX v3.00 */
#include <pic16f1829.h> //Not required but this is the reference used by "C" for names and location on uC
#include <htc.h> //refers on HiTech C, Microchip purchased HiTech
#define _XTAL_FREQ 4000000 //Used by the XC8 delay_ms(x) macro
#define switch PORTAbits.RA2 // Can use RA2 instead of PORTAbit.RA2 to define pin attached to switch
//instead of saying PORTAbits.RA2 each time
//config bits for the PIC16F1829
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=OFF, MCLRE=OFF, CP=OFF, CPD=OFF, BOREN=ON, CLKOUTEN=OFF, IESO=OFF, FCMEN=OFF
#pragma config WRT=OFF, PLLEN=OFF, STVREN=OFF, LVP=OFF
//Initialization subroutine
void initialize(void) {
ANSELC=0; //All pins of Port C are digital I/O
ANSA2=0; //switch pin, RA2, is digital IO
TRISA2 = 1; //switch is an input
TRISC = 0; //all pins of Port C are outputs
OSCCON = 0b01101000; // 4 MHz
}
unsigned char i1; //only need 4 bits to count to 16. unsigned character variable is 8 bits long
// Here is main(). There are many ways to do this 4-pin (LED) sequence
void main(void)
{
initialize();
i1=1; //Start the main program with the variable =1. Could have done this during its definition
while (1) //runs continuously until MCU is shut off
{
if (switch==1) //Button not pressed pin at 5V
{ i1=1; }
while (switch==1) //Button not pressed
{
PORTC=i1; //Note that writing to PORTC writes to LATC
__delay_ms(500);
i1=i1*2;
if (i1==16)
{ i1=1; }
}
if (switch==0) //Button pressed pin at ground
{ i1=8; }
while (switch==0) //Button pressed
{
PORTC=i1;
__delay_ms(500);
i1=i1/2;
if (i1==0)
{ i1=8; }
}
}
}
a. Instead of using the multiply and divide functions to change
pins/LED’s, use the << and >> functions. References: Deitel and Deitel
“C, How to Program and
https://en.wikipedia.org/wiki/Operators_in_C_and_C
Left shift value << n is integer multiplication of value by 2^n or value*(2^n)
Right shift value >> n is integer division of value by 2^n or value/(2^n))
When you have some var and you use one of the shift operators, you are taking the value of whatever var is and shifting the binary digits (bits) that represent it's value to the left or the right.
A basic example of this:
uint8_t var = 1; //0b00000001 in binary
var <<= 1; //var is now 0b00000010, that is 1*(2^1) or 2
var >>= 1; //var is now 0b00000001, that is 2/(2^1) or 1
There is a huge caveat for using the shift operator and that is that whenever you shift bits you are filling 0s in from the opposite direction that you are shifting so you have to pay attention to the integer size.
uint8_t var = 1;
var <<= 4; //var is now 0b00010000, 4 zeros filled in on the right
var = 1;
var <<= 8; //var is now 0b00000000, because 8 zeros were filled in on the right!
Now with regard to how you use this to manipulate the pins on a microcontroller, you would take some variable that increments or decrements and shift left or right by that variable and assign the resulting value to the register in the module that controls that pin, which in this case is the PORTx module. In your code that would look like this:
if (switch == 1) //Button not pressed pin at 5V
{
i1 = 0; //initialize to 0
}
while (switch == 1) //Button not pressed
{
PORTC = (1 << i1++); //set will set just one pin at a time, the first will be pin 0, the next pin 1, and so on
__delay_ms(500);
if (i1 == 8){
i1 = 0; //reset variable
}
}
if (switch == 0) //Button pressed pin at ground
{
i1 = 0; //initialize to 0
}
while (switch == 0) //Button pressed
{
PORTC = (0x80 >> i1++); //this will set 1 pin at a time, the first will be pin 7, the next will be pin 6, and so on
__delay_ms(500);
if (i1 == 8)
{
i1 = 0; //reset variable
}
}
b. Change the clock frequency in the program to 1 MHz and make the
on/off time of each LED .1 seconds. This should make the rotation
visibly faster. (Remember to change the _XTAL_FREQ value since this is
used for the __delay_ms() function built into XC8)
This portion of your code:
OSCCON = 0b01101000; // 4 MHz
Actually configures the frequency of the oscillator used by the microcontroller for its clock signal. However, it is important for you to know the source of that clock signal, which according to the datasheet is controlled by Configuration Word 1. This is set in the #pragma config FOSC=INTOSC portion of your code.
To obtain 1 MHz you will want to change that line to this:
OSCCON = 0b01011000; // 1 MHz
This is found in the OSCCON register description in the datasheet.
The __delay_ms function uses the _XTAL_FREQ to calculate a delay which is why you are being told to change this line of your code:
#define _XTAL_FREQ 4000000
To this
#define _XTAL_FREQ 1000000

Arduino CTC timer interrupt ~2x fast

Afternoon all
I'm looking for some assistance please with something that has been confusing me whilst trying to learn timer interrupts
You'd be best treating me as a novice. I have no specific goal here other than learning something that I think would be useful feather to add to my cap!
I have written the below sketch as a stab at a rigid framework for executing different fcns at different rates. I've done something similar using millis() and whilst that worked I found it inelegant that a) there was no obvious way to check for task overruns and backing-up the execution rate and b) the processor is bunged up by checking millis() every program cycle.*
Essentially what I think should be a 1ms timer interrupt on Timer2 (16MHz/64 prescaler /250 compare register =1000hz) is coming out around 0.5ms. I've been confused for hours on this but I'm prepared to accept it could be something fundamental/basic!
What's also throwing a spanner in the works is that using serial comms to try and debug the faster task rates seems to slow things down considerably, so I'm inferring the problem by counting up 1ms tasks to call 10,100 and 1000ms tasks and debugging at the slower level. I suppose chewing through a few characters at 9600baud probably is quite slow.**
I've pasted the code below. Any pointers highly appreciated. Be as harsh as you like :)
cheers
Al
*Whilst not what I'm confused about - any comments on my logic here also welcome
** Although I don't get how Serial.println manages to slow the program down. It's driven from interrupts so it should surely just drop the comms and perform the next ISR - effectively a task overrun. Any comments here also welcome
//NOTES
//https://www.robotshop.com/letsmakerobots/arduino-101-timers-and-interrupts
//https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328
//http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_Datasheet.pdf
//
//INITIALISE EVERYTHING
const int ledPin = 13;
volatile int nStepTask1ms = 0; // init 0 - to be used for counting number of 1ms tasks run and hence calling 10ms task
volatile int nStepTask10ms = 0;
volatile int nStepTask100ms = 0;
volatile int nStepTask1000ms = 0;
volatile int LEDFlashState = 0; // init 0 - variable to flip when LED on
volatile int millisNew = 0; // to store up to date time
volatile int millisOld = 0; // to store prev time
volatile int millisDelta = 0; // to store deltas
//
void setup()
{
Serial.begin(9600); //set up serial comms back to PC
pinMode(ledPin,OUTPUT); //to flash the embedded LED
noInterrupts(); //turn off interrupts while we set the registers
//set up TIMER first
TCCR2A = 0; //sets TCCR1A byte to zero, bits to be later individually mod'd
TCCR2B = 0; //sets TCCR1B byte to zero, bits to be later individually mod'd
TCNT2 = 0; //ensures counter value starting from zero
Serial.println("Timer1 vars reset");
TCCR2B |= (1<<WGM12); // bitwise or between itself and WGM12. TCCR2B = TCCR2B | 00001000. Sets WGM12 high. (CTC mode so WGM12=1, WGM 13,11,10 all 0) https://stackoverflow.com/questions/141525/what-are-bitwise-shift-bit-shift-operators-and-how-do-they-work
Serial.println("Mode 4 CTC set");
Serial.println("TCCR2B=");
Serial.println(TCCR2B,BIN);
TCCR2B |= (1<<CS11); // sets CS11 high
TCCR2B |= (1<<CS10); // sets CS10 high (i.e. this and above give /64 prescaler)
Serial.println("Prescaler set to 64");
OCR2A = 250; //compare match register for timer2
Serial.println("Compare Register set");
Serial.println("OCR2A=");
Serial.println(OCR2A);
TIMSK2 |= (1 << OCIE2A); //enables interrupts - https://playground2014.wordpress.com/arduino/basics-timer-interrupts/
Serial.println("Interrupt Mask Register Set");
Serial.println("TIMSK2=");
Serial.println(TIMSK2);
interrupts(); //enable interrupts again - not sure if this is required given OCIE1A being set above?
}
//set up ISR for Timer2 - timer structure called every interrump (1ms) that subsequently calls 1,10,100 and 1000msec task fcns
ISR(TIMER2_COMPA_vect)
{
TASK_1ms();
if (nStepTask1ms>9)
{
TASK_10ms();
if (nStepTask10ms>9)
{
TASK_100ms();
if (nStepTask100ms>9)
{
TASK_1000ms();
}
}
}
}
void TASK_1ms()
{
// 1ms tasks here
nStepTask1ms++;
}
void TASK_10ms()
{
//10ms tasks here
nStepTask1ms=0;
nStepTask10ms++;
}
void TASK_100ms()
{
//100ms tasks here
nStepTask10ms=0;
nStepTask100ms++;
//Serial.println(nStepTask100ms);
}
void TASK_1000ms()
{
//1000ms tasks here
nStepTask100ms=0;
//do something
changeLEDFlashState();
//check timing tick of this task
millisNew=millis();
millisDelta=millisNew-millisOld;
Serial.println(millisDelta);
millisOld=millisNew;
nStepTask1000ms++;
}
void changeLEDFlashState()
{
if(LEDFlashState==0)
{
digitalWrite(ledPin,HIGH);
LEDFlashState=1;
//Serial.println("LED Turned On");
}
else
{
digitalWrite(ledPin,LOW);
LEDFlashState=0;
//Serial.println("LED Turned Off");
}
}
void loop()
{
// empty
}
You have two lines here:
TCCR2B |= (1<<CS11); // sets CS11 high
TCCR2B |= (1<<CS10); // sets CS10 high (i.e. this and above give /64 prescaler)
These two lines set the lower three bits of TCCR2B to 011, which is a /32 prescaler.
Note that for Timer1 and Timer2, the prescaler settings are different than Timer0.
For Timer0, the settings above would give you a /64 prescaler.

AVR Programming - How to read in consecutive button presses in C

Here's what I have to dp:
Consider an ATmega324A development board and a CSSE2010/CSSE7201 IO Board. Switches
S3 to S0 are connected to AVR port B pins 3 to 0. Push button B0 is connected to AVR port A
pin 0. LEDs L0 and L2 and connected to AVR port C pins 0 and 2 respectively.
LED L0 (red) is the “Locked” LED and should only be on when the lock is locked. LED L2
(green) is the “Unlocked” LED and should only be on when the lock is unlocked. The lock
initially starts in the locked state. The user enters the binary code for a digit on the switches (S3 to S0) and then presses and releases push button B0 to “enter” the first digit. The user then enters the binary code for the second digit on the switches and presses and releases push button B0 to “enter” the second digit. If the digits match the expected value (the last digit of your student number followed by the third digit of your student number) then the lock should be “unlocked”, otherwise it should stay in the locked state until the two digits are entered correctly.
Here is my code so far:
#include <avr/io.h>
/* Seven segment display values */
uint8_t seven_seg[16] = { 63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};
int main(void) {
uint8_t digit;
uint8_t temp;
uint8_t digit2;
uint8_t code[2] = {6,3}
DDRA = 11111110; //port A is input (last bit)
DDRB = 0X00; //port B is input
DDRC = 0x0F; //port c is output
DDRD = 0XFF; //set port D to be output
while(1) {
/* Read in a digit from lower half of port C pins */
/* We read the whole byte and mask out upper bits */
PORTB = 1; //Led is red
clock = PINA & 00000001; //read in last bit of port A
temp = PINB & 0X0F; //read in lower half of port b
/* Checks to see the first digit is correct */
if(temp == code[0] && clock == 1) {
digit = temp;
PORTD = seven_seg[temp];
} else {
PORTD = 0;
}
}
I'm getting stuck at the point where I have to read in the second digit. Would I be doing this inside a nested loop of the first? Or how would I go about reading in two digits from my switches, which is clocked in each time from the press of a button?
To make a variable program that can be used for longer number sequences, simply use a loop. for(uint8_t i=0; i<NUMBER_OF_DIGITS; i++). The port reading needs to be inside the loop.
However, you cannot read buttons the way you do. All buttons have an electro-mechanical signal bounce, which you need to filter out to prevent false readings. You must do this on any kind of embedded system.
The simplest way to do this is to sample the button once, save the result, wait a few milliseconds, then sample it again. If the samples compare equal, accept it as result (pressed or not pressed).
Alternatively, you can trigger an interrupt on the edge of the button signal, from there start a timer, and then when the timer runs out, read the port.
More advanced methods use some form of median filters.
So if i get you right you wanna do something like this
Start:
Wait for pushbutton
Wait for first digit
Wait for pushbutton
Wait for second digit
If digits they are the same
Turn off Red LED
Turn on Green LED
Else
Goto start
Can you confirm this? It is always a good thing to do your code in pseudo first. It gives very good overview of what you want to do.

Resources