I'm trying to get myself started with the PIC 18F4550 using the C, MPLAB X (both IDE and IPE) and using the PICKit 3.
I've managed to blink one LED without any problems, but as I try to blink more than one LED at simultaneously, it doesn't work.
Please note that I will post my full code at the end of the question. Until then, I'll be writing pseudocode in hope of making my question a little bit clearer.
Assume I want to blink 4 LEDs, each attached to an output pin of the chip, you'd obviously type something like
loop{
output1 = 1;
output2 = 1;
output3 = 1;
output4 = 1;
delay();
output1 = 0;
output2 = 0;
output3 = 0;
output4 = 0;
delay();
}
You would expect that all of the LEDs would turn on and off simultaneously. However, I noticed that only the LED connected to output4 would blink and the rest would remain turned off.
So I tried flipping the order of the output pins as such
loop{
output1 = 1;
output2 = 1;
output4 = 1;
output3 = 1;
delay();
output1 = 0;
output2 = 0;
output4 = 0;
output3 = 0;
delay();
}
As a result, only the LED attached to output 3 would blink, and the rest would remain turned off.
So I figured, somehow, the code is not executing sequentially as I'd expected it to do so.
Can anyone please provide me with an explanation and a possible solution for this?
Thanks a lot!
Here's the full code
#include <xc.h>
#include <p18f4450.h>
#pragma config FOSC = HS
#define outRed PORTBbits.RB0
#define outBlue PORTBbits.RB1
#define outYellow PORTBbits.RB2
#define outGreen PORTBbits.RB3
#define _XTAL_FREQ 10000000
void delay(unsigned int);
void main(void) {
TRISBbits.TRISB0 = 0;
TRISBbits.TRISB1 = 0;
TRISBbits.TRISB2 = 0;
TRISBbits.TRISB3 = 0;
while(1) {
outRed = 1;
outGreen = 1;
outBlue = 1;
outYellow = 1;
delay(1000);
outRed = 0;
outGreen = 0;
outBlue = 0;
outYellow = 0;
delay(1000);
}
}
void delay(unsigned int delayInput) {
unsigned int mul = delayInput/50;
unsigned int count = 0;
for (count = 0; count <= mul; count ++)
__delay_ms(50);
}
This could be a LATCH issue. I have had this problem a few times when I started up. Try writing to the LATB (output latch) register instead of the PORTB register. I always use the LATx for output and the PORTx for input.
Always write to the output latches (in your case LATB) and read inputs from PORTx. Writing to PORTx has unpredictable behaviour.
Related
I am currently working on transmitting from a PIC18F4620, through a FT232, to CoolTerm. I am currently only receiving FF and FE from the PIC though. I was wondering why this may be the case. The Rx - TX are correctly switched, the cable connecting them appears to be secure. The only issue I can think of is the baud rate would be incorrect, But looking at the datasheet I don't believe it is. any insight would be greatly appreciated. (I arbitrarily chose 51 as my test number. any number or letter would work).
Circuit
const unsigned char MSG0[] = "Transmitting... ";
const unsigned char MSG1[] = "Sent:";
const unsigned char MSG2[] = "TEST:";
// Subroutine Declarationsb
#include <pic18.h>
// Subroutines
#include "lcd_portd.c"
#include <delays.h>
#include <plib.h>
#include <stdint.h>
void UART_TX_Init(void)
{
BRG16 = 0;
BRGH = 1; // Set For High-Speed Baud Rate
SPBRG = 64; // Set The Baud Rate To Be 9600 bps
//--[ Enable The Ascynchronous Serial Port ]--
SYNC = 0;
SPEN = 1;
//--[ Set The RX-TX Pins to be in UART mode (not io) ]--
TRISC6 = 1; // As stated in the datasheet
TRISC7 = 1; // As stated in the datasheet
TXEN = 1; // Enable UART Transmission
}
void UART_Writes(uint8_t data)
{
while(!TRMT);
TXREG = data;
}
// Main Routine
void main(void)
{
UART_TX_Init();
unsigned int result = 0;
unsigned int i;
uint8_t data = 51;
TRISA = 0;
TRISC = 0;
TRISB = 0;
TRISD = 0;
TRISE = 0;
TRISA = 0;
TRISB = 0x00;
PORTC = 0;
PORTD = 0;
PORTE = 0;
ADCON1 = 0x0F;
LCD_Init(); // initialize the LCD
LCD_Move(0,0); for (i=0; i<20; i++) LCD_Write(MSG0[i]);
Wait_ms(100);
LCD_Move(1,0); for (i=0; i<5; i++) LCD_Write(MSG1[i]);
while(1) {
Wait_ms(1000);
UART_Writes(data);
LCD_Move(1,5); LCD_Out(data,3,0);
}
}
CoolTerm
What is your clock frequency and how did you configured your crystal oscillator setup ? please share your clock setting and configuration.
If clock setting is fine then calculate the correct baud-rate and try.
void UART_Writes(uint8_t data)
{
while(!TRMT)
{
//put Nop() and try
}
TXREG = data;
}
I am interfacing LM35 with Atmega8. To display digits I use 7 segment LED anode display that I connect to AVR both ends (it handles it without transistors so why not). Strange thing happens:
res value after assigning it from adc is 237 (23.7 degrees). I want to print on my display the first digit (2).
If I leave last line in the while commented out, the display first shows digit 2 correctly but after the first delay it shows 1 instead of 2. Otherwise I get correctly digit 2. Why is this happening?
#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU
#include <avr/io.h>
#include <util/delay.h>
#define DELAY_IN_MS 500 /* 0.5 sec */
int numbers[] = {
0b01000000,
0b01110011,
0b00100100,
0b00100001,
0b00010011,
0b00001001,
0b00001000,
0b01100011,
0b00000000,
0b00000001,
0b11111111 // off
};
uint8_t digits[3];
void initADC()
{
ADMUX=(1<<REFS1)|(1<<REFS0);
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while(!(ADCSRA & (1<<ADIF)));
//Clear ADIF by writing one to it
ADCSRA|=(1<<ADIF);
return(ADC);
}
int main()
{
DDRD = 0xFF;
PORTD = 0xFF;
DDRB = 0b00000001;
PORTB = 1;
initADC();
uint16_t adc_value;
uint16_t res;
while(1)
{
adc_value = 0;
for (int i = 0; i < 250; i++)
{
adc_value += ReadADC(0);
}
adc_value=(adc_value/25)/4;
res = adc_value;
for(int j = 2; j >= 0; j--) {
digits[j] = res%10;
res /= 10;
}
uint8_t dig = digits[0];
PORTD = numbers[dig];
_delay_ms(DELAY_IN_MS);
// if following is uncommented there blinks digit two correctly
// if commented there is unblinking digit 1
PORTD = numbers[10]; // display off
}
return 0;
}
The problem was induction.
My circuit had many wires in non-soldering-field. When the display was on, there was a lot of induction going on changing resulting voltage on ADC input/LM35 output.
There is more than one solution.
1) Software: I moved ADC conversion into the interruption function. It turns of the displays, converts value from lm35 and displays digit on proper display. It happens so fast that the eye cant perceive it.
I prefer this one for now, because it makes my circuit simpler.
2) Hardware: adding L/C or R/C filter to adc pin should resolve the issue.
Full code for 1)
#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define DELAY_IN_MS 5000 /* ms */
#define NUM_OF_MEASUREMENTS 100
#define NUM_DISPLAYS 3
int numbers[] = {
0b10000001,
0b10011111,
0b10100100,
0b10010100,
0b10011010,
0b11010000,
0b11000000,
0b10011101,
0b10000000,
0b10010000,
0b11111111 // off
};
int display = 0;
uint8_t digits[NUM_DISPLAYS];
volatile uint16_t adc_values[NUM_OF_MEASUREMENTS];
int adc_read_cycle_index = 0;
uint32_t res;
void initADC()
{
ADMUX=(1<<REFS1)|(1<<REFS0);
ADCSRA=(1<<ADEN)|(1<<ADPS2);
}
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while (ADCSRA & (1<<ADSC));
return(ADC);
}
void readDegrees()
{
adc_values[adc_read_cycle_index] = (ReadADC(0)*10)/4;
if(adc_read_cycle_index + 1 == NUM_OF_MEASUREMENTS) {
adc_read_cycle_index = 0;
} else {
adc_read_cycle_index++;
}
}
void fetchTemperatureDigits() {
res = 0;
for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
res += adc_values[i];
}
res /= NUM_OF_MEASUREMENTS;
for(int j = 2; j >= 0; j--) {
digits[j] = res%10;
res = res / 10;
}
}
void initTimer0()
{
// Prescaler = FCPU/64
TCCR0|=(1<<CS01);//|(1<<CS00);
//Enable Overflow Interrupt Enable
TIMSK|=(1<<TOIE0);
//Initialize Counter
TCNT0=0;
}
ISR(TIMER0_OVF_vect)
{
// turn off displays
PORTD = numbers[10];
// read ADC and convert to degrees
readDegrees();
// turn on proper anode
PORTB &= 0b11111000;
PORTB |= (1<<display);
// show digit
PORTD = numbers[digits[display]];
// show decimal point for second display (21.5 - second display shows "1.")
if(display == 1) {
PORTD &= 0b01111111;
}
// next display for next interruption
display++;
if(display == NUM_DISPLAYS) {
display = 0;
}
}
int main()
{
initADC();
for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
readDegrees();
}
DDRD = 0xFF;
PORTD = 0;
DDRB |= 0b00000111;
PORTB |= 1;
initTimer0();
sei();
while(1) {
fetchTemperatureDigits();
_delay_ms(DELAY_IN_MS);
}
return 0;
}
I'm programming my C code onto a PIC board. My question is how to get my program to count how long a button (RB0) is pressed down. It then displays the time it took and display it on an LCD Display. It counts in ms. Below is my code so far.
// Global Variables
unsigned int COUNTER;
// Subroutine Declarations
#include <pic18.h>
#include "lcd_portd.c"
//LCD routine, modified from previous examples
void LCD_Out(unsigned int STUFF, unsigned char A)
{
unsigned char C[5], i;
for (i=0; i<5; i++)
{
C[i] = STUFF % 10;
STUFF = STUFF / 10;
}
for (i=5; i>0; i--)
{
if (i == A) LCD_Write('.');
LCD_Write(C[i-1] + '0');
}
}
void interrupt IntServe(void)
{
if (TMR2IF)
{
RB0 = !RB0;
TMR2IF = 0;
COUNTER = COUNTER + 1;
}
}
// Main Routine
void main(void)
{
//Instantiate all Ports to ready for Timers and pin sets
LCD_Init();
TRISA = 0;
TRISB = 0;
TRISC = 0;
TRISD = 0;
TRISE = 0;
ADCON1 = 0x0F;
//Timer Interrupt for 1 ms, A = 9, C = 4, B = 250
//Which means PR2 = 249, and # is x1001101 = 0x4D
T2CON = 0x4D;
PR2 = 249;
TMR2IE = 1;
PEIE = 1;
TMR2ON = 1;
TMR2IP = 1;
GIE = 1;
// While Loop displays length of Wait through Counter
while(1)
{
LCD_Move(0,0);
LCD_Out(COUNTER,3);
}
}
you need figure out how your button is connected to your microcontroller and declare that port in you main function. Make sure you are able to read the state of the port (i.e. button up or button down).
Then it your IntServe interrupt check if the button is down, if it is increment COUNTER.
The COUNTER is an integer, so it will only be able to count upto 65,535 ms (about 65 seconds), you may want to implement a second count variable for overflow, so that you can count button presses longer than 65 seconds.
so I am currently trying to Read and Write the time to the RTC and these methods will just not work.
this is what I have:
so it sends the address byte to determine where it would like to read from then flips the pin to input and reads each bit out until the byte is complete.
char ReadByte(char ByteToRead)
{
RB0 = 0; // ensure CLK low
RB5 = 1;
char received = 0;
int i;
for(i =0;i < 8 ;i++)
{
RB4 = ByteToRead & 1;
RB0 = 0;
ByteToRead >>= 1;
RB0 = 1;
}
TRISB4 = 1;//B4 = input port
for(i =0;i < 8 ;i++)
{
received |= RB4;
RB0 = 1;
received <<= 1;
RB0 = 0;
}
RB5=0;
TRISB4 = 0;
return DecimalToBCD(received);
}
the second sample is my write method:
void WriteByte(char ClockReg ,char data)
{
RB0 = 0; // ensure CLK low
RB5 = 1; // raises RST bit
int i;
for(i =0;i < 8 ;i++)
{
RB4 = ClockReg & 1;
RB0 = 0;
ClockReg >>= 1;
RB0 = 1;
}
for(i =0;i < 8 ;i++)
{
RB4 = data & 1;
RB0 = 0;
data >>= 1;
RB0 = 1;
}
RB5 = 0;
__delay_us(1);
RB5 = 1;
}
this does the same for the first bit to determine the register the write the value you give it.
the only help I can find online are links to a 2 year old page where there is no code or help just people asking to be PMed. So please if you can help
Thanks in advance
I'd work on the ReadByte method before you work on the WriteByte because then you can validate the write by reading it back. So, looking at the ReadByte routine, I can quickly see one problem.
The datasheet for the DS1302 says "data bits are output on the falling edge of clock" but in your ReadByte routine, the input on RB4 is read and then later you set the clock low. Also "Note that the first data bit to be transmitted occurs on the first falling edge after the last bit of the command byte is written." but your first time through the loop, you are reading RB4 before the first falling edge.
To be confimed !
I believe it is because I had not disabled the Write protect bit also for future reader the Clock Halt flag (CH) is to be cleared not set.
Hope this helps anyone looking for it
Dan
I'm using the pic18F4550 with microchip v8.63 and with the C 18 compiler. I'm using a LDR that retrieve the value of the led (not on my picdem board) (red, green and blue) these values are stored in a variable after each conversion. Afther that when I press the button S2, I come into the method ISR: this part works.
But now: I try to compare the variable red, green and blue in the if's: but I think that it not happen, he just go to my 'else' (led RB3 on my picdem board burns).
#include <p18f4550.h>
/** V E C T O R R E M A P P I N G *******************************************/
extern void _startup (void); // See c018i.c in your C18 compiler dir
#pragma code _RESET_INTERRUPT_VECTOR = 0x001000
void _reset (void)
{
_asm goto _startup _endasm
}
#pragma code
void ISR (void);
#pragma code _HIGH_INTERRUPT_VECTOR = 0x001008
void _high_ISR (void)
{
_asm goto ISR _endasm
}
#pragma code _LOW_INTERRUPT_VECTOR = 0x001018
void _low_ISR (void)
{
;
}
#pragma code
/******************************************************************************/
// global variable, value off LDR.
unsigned int var1ADRESH = 0x00;
unsigned int color_red = 0;
unsigned int color_green = 0;
unsigned int color_blue = 0;
void main (void)
{
TRISD = 0x00; // PORTD als uitgang
RCONbits.IPEN = 0; // prioriteit uit
INTCONbits.GIE = 1; // enable interrupt
INTCONbits.RBIE = 1; // interrupt portB aan
//= set up port =
TRISAbits.TRISA0 = 1; // Set RA0/AN0 to input
//leds
TRISAbits.TRISA3 = 0;
TRISAbits.TRISA4 = 0;
TRISAbits.TRISA5 = 0;
LATAbits.LATA3 = 1;
LATAbits.LATA4 = 1;
LATAbits.LATA5 = 1;
ADCON0 = 0b00000000; // Set channel select to AN0
ADCON1 = 0b00001110; // Configure RA0/AN0 as analogue
ADCON2 = 0b10101010; // Right justified result
// TAD 12 and FOSC 32 - may need to adjust this
// depending on your clock frequency (see datasheet)
while(1)
{
_asm sleep _endasm
}
}
#pragma interrupt ISR
void ISR (void)
{
if (INTCONbits.RBIF==1) {
//conversie blauw
LATAbits.LATA3 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_blue = ADRESH; //waarde in blauw
//conversie rood
LATAbits.LATA3 = 1;
LATAbits.LATA4 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_red = ADRESH; //waarde in blauwe steken
//conversie groen
LATAbits.LATA4 = 1;
LATAbits.LATA5 = 0;
ADCON0bits.ADON = 1; // Enable ADC
// read LDR value.
ADCON0bits.GO = 1; // Set the GO bit of the ADCON0 register to start the conversion.
while (ADCON0bits.GO); // Wait until the conversion is complete.
ADCON2bits.ADFM = 0; // read result as 8-bit. (dus data in ADRESH) !
//= read data in ADRESH =
var1ADRESH = ADRESH; // reading value from LDR
color_green = ADRESH; //waarde in blauwe steken
// alles uitzetten
//PORTB = 0b1111111;
LATAbits.LATA5 = 1;
if(color_blue > color_red && color_blue > color_green){
//blauw
LATDbits.LATD0 = 1;
}
if(color_red > color_blue && color_red > color_green){
//rood
LATDbits.LATD1 = 1;
}
if(color_green > color_red && color_green > color_blue){
//groen
LATDbits.LATD2 = 1;
}
else {
LATDbits.LATD3 = 1;
}
}
INTCONbits.RBIF = 0;
}
Try to set all three of LATA3,4,5 before every readout?
Set TRIS for D0..2
I detect a few weird things (but I confess none might be the source).
Once you set the LATA bits, does the LDR immediately changes val? Of course not, but is it a matter of ns, us or ms?... I don't see any delays for LDR settling times.
You are always sleeping so it's probably required to set the ADON=1 (check DS). However, check the device DS for how long does the AD needs to get it's internals up and ready after setting ADON=1. On that subject, once ADON=1 you shouldn't and don't need to set it over again the the next two acquisitions.
The same thing about the format of the result. Set it once, and do it before the GO=1. Don't re-set every conversion.
If you're using only 8bit results, why the unsigned int? (btw, int is pretty dangerous in microcontrollers. Use short and chars and always explicit signdness).
Why the var1ADRESH assignment then color_xxx? when do the LATD bits eventually get =0?
Your If else only works on the last branch. I believe you intended a if{} else if{} else if... structure.