pic 16f84a timer / counting code not behaving as expected - c

I am basically learning about pic external interrupts on the pic16f84a microcontroller. Basically i want to count every press of a button attached to pin B0 (RB0/T0CKI) and display results from a seven segment display. I am using hitech c compiler.
#include<htc.h>
__CONFIG(WDTE_OFF& PWRTE_OFF & CP_OFF);
char get7SegmentCode(char value)
{
switch(value)
{
case 0b00000000:
return (char)0b00111111 ; //0 code
case 0b00000001:
return (char)0b00110000 ; //1 code
case 0b00000010:
return (char)0b01011011 ; //2 code
case 0b00000011:
return (char)0b010011111 ; //3 code
default:
return (char)0b00000000 ; //all code
}
}
void main()
{
//declare portb for output and porta upper 4 pins for output and lower 4 for input
TRISA=0b00001111;
TRISB=0b00000000;
CLRWDT();//clear watchdog timer
TMR0=0;
OPTION_REG = 0b00111000;//RBPU:PORTB pull-ups are disabled,
//INTEDG:Interrupt on falling edge of RB0/INT pin
//T0CS:Transition on RA4/T0CKI pin
//T0SE:Increment on high-to-low transition on RA4/T0CKI pin
//PSA:Prescaler assigned to the WDT
while(1)
{
char timerval= TMR0;//read tmr0 into variable
char restrictedtimerval= timerval & 0x0f;//force upper 4bits to zero to restrict value to 0 - f
PORTB= get7SegmentCode(restrictedtimerval);
}
}
Unfortunately when I simulate this code in proteus vsm, the seven segment display just shows '0' no matter how many times i press the switch. Why is this happening?
PS: This is my circuit.

Pin RA4/T0CKI is fifth bit named RA4 of port A.
So your initiation code must be
TRISA=0b00011111;
instead
TRISA=0b00001111;

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).

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

Interfacing a 16x2 LCD with SPI, Getting Characters into the Second Row

I'm using an MSP430 MCU to read analog signals and display the results on an LCD with a SPI connection. The LCD is a 16x2 that is connected according to the SPI connection details on the Datasheet and uses a Hitachi HD44780 driver. I can fill up the 16 characters of the first row no problem. When I go over 16, the last character does not display(as expected) even if I extend the char array that holds the string that I want to print. The problem is that the second row never displays anything. When there are no characters in a position in the first row, there is still a faint background in all positions, but the second row is always continuously blank. Below are the functions that are used in printing. What am I doing wrong?
I know the wiring is correct and the LCD is functional. To test these, I wired the display to an arduino to test, since the code is much easier and I was able to display characters in bow rows. The non-descriptive variables are defined by the MSP430 source files and include registers, buffers, and other controls to put the device in SPI communication mode.
void AlignLaserTarget()
{
int i,k, j;
struct testResults *ptestResults;
char mess1[17]; //changed from 8 to hold 16 characters
ptestResults=getTestResults();
// reset global vars
timeI1=0;
timeA=0;
i=starResults.ch1Amplitude; //analog integer value to be printed on LCD
j=starResults.ch2Amplitude; //same
k=starResults.ch3Amplitude; //same, but should go in second row
sprintf(mess1,"1:%i 2:%i", i, j);
stringTo_lcd8( mess1);
}
void stringTo_lcd8( char* lcdString )
{
int i;
LCD_COMMAND_MODE; // display code
timer_us(20);
write_lcd8( 0x01); // clear display
timer_ms(2);
LCD_DATA_MODE; //enable data sending pin
for ( i=0; *lcdString !=0 ; ++i)
{
write_lcd8( *lcdString);
++lcdString;
} // end of display code
timer_us(10000); // 10ms delay . should not be needed as normal interval between counts is at least 75 ms or 12 in. at 800ft/min rate
}
//*******************************************************
void write_lcd8( unsigned char lcdbyte)
{
UCA0IFG &= ~UCTXIFG; // CLEAR FLAG
UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
UCA0CTL0 = UCMST+UCSYNC+ UCMSB+ UCCKPH;
UCA0BR0 = 0x80; // /hex80
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
timer_us(100);
LCD_CHIP_ENABLE; // LCD enable pin output
timer_us(20); // added trp
UCA0TXBUF =lcdbyte;
timer_us(150);
while (!(UCA0IFG&UCTXIFG));
UCA0IFG &= ~UCTXIFG; // CLEAR FLAG
LCD_CHIP_DISABLE;
timer_us(30);
UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
UCA0CTL0 |= UCMST+UCSYNC+ UCMSB+ UCCKPH;
UCA0BR0 = 0x02; // /2
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
}
I think you have to design some more driver function before using for application logic. I have taken sample code to set cursor position
void Lcd8_Set_Cursor(char line, char col)
{
if(line == 1)
Lcd8_Cmd(0x80 + col);
else if(line == 2)
Lcd8_Cmd(0xC0 + col);
}
Then use same in your printing logic. When ever length exceeding 16 you can switch line and start writing.
The HD44780 is expecting 40 characters per row, so I just added spaces to fill up the first row and then wrote the characters for the next row. I'm sure there is a better solution but this was fast and it worked. I also had to change the initialization specifications for a two row configuration.

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.

enable ports RB4 and RB3 on pic 18F4550

I have written this program in mplab v8.63 with c compiler C18 on the pic 18F4550. if I press the button on my picdem (S3) and there is a led connected on RB5 (with a resistor) then the led goes on. When i pressed on (S3) and there is a led on RB4, the the led will not be on (while I expected this) the same with RB3. Thit I forgot something to set?
The goal is for the red, green and blue LED separately measured with a LDR. but first I obviously must enable the ports RB5, RB4 and RB3.
#pragma code
/******************************************************************************/
void main (void)
{
TRISD = 0x00; // PORTD as output
TRISB = 0b00110000; // RB4 en RB5 as input
TRISA = 0x00; // RA output
RCONbits.IPEN = 0; // priority
INTCONbits.GIE = 1; // enable interrupt
INTCONbits.RBIE = 1; // interrupt portB on
while(1)
{
_asm sleep _endasm
}
}
#pragma interrupt ISR
void ISR (void)
{
//int red= 01110010011011110110111101100100;
int on = 1;
int off = 0;
if (INTCONbits.RBIF==1)
{
if(PORTBbits.RB5==0) // S3 pressed?
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
if(PORTBbits.RB4==0)
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
if(PORTBbits.RB3==0)
{
LATDbits.LATD1 ^= 1; // D2 toggle
LATAbits.LATA2 ^= on;
}
}
INTCONbits.RBIF = 0;
}
In the PIC TRIS registers, a bit set to 1 means input and 0 means output. You set TRISB = 0b00110000 which is all outputs on port B apart from RB5 and 4 which are inputs. If you need RB3 as a digital input as well, you need to set TRISB = 0b00111000. The bits are counted b7,b6,b5,b4,b3,b2,b1,b0.
However, you are expecting RB3 to trigger the RBIF which it does not as I explained here. So as you have it, only RB5 and 4 would trigger RBIF, but if RB3 was low at that time it would toggle D1 and A2. So move this code to main loop as I suggested until you get it working, then make it work on interrupt.
As ChrisJ says, some pins default to analog input unless you disable it so you wont get the digital input and interrupt on pin change that you are expecting using ADCON1
If the PBADEN bit is set, PORTB is initialized with RB4:RB0 set as analog inputs. If so you must use ADCON1 to disable analog inputs on RB4:RB0. The example program at the beginning of Section 10.2 of the manual wites 0x0E to ADCON1 to properly initialize PORTB.
See also Section 21.0 for an explanation of ADCON1 bits.

Resources