PORTB's LEDs controlling via input PINA in avr - avr

I'm trying to write a code in Atmel so I can control atmega32 PORTB's pins via switches connected
to PINA, it works but there is only one problem, if one switch is left high and an another switch
turns high the led connected(relates) to the second pin doesn't change, where did i go wrong?
#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 1000000UL //Setting the CPU frequency to 1MHz
toggle(void){ //Creating a toggle function to call it within the main function
unsigned char input = 0x00; //Creating an 8-bit variable so we can save the input port
int t = 1; //Loop variable
while(PINA != 0x00){
t = t + 1;
_delay_ms(1);
if(t == 100) break;
}
while(t != 100){
if((PINA != 0x00) && (input !=PINA))
{
input = PINA;
PORTB = PORTB^input;
t = t + 1;
_delay_ms(1);
}
else if(input == PINA)
{
t = t + 1;
_delay_ms(1);
}
else if(PINA == 0x00){
t = t + 1;
_delay_ms(1);
input = PINA;
}
}
}
int main(void)
{
DDRA = 0x00; //Setting PORTA as input
DDRB = 0xFF; //Setting PORTB as output
DDRC = 0xFF; //Setting PORTC as output
DDRD = 0x02; //Setting PORTD.2 as output
PORTB= 0x00; //Setting PORTB's output to zero
PORTC= 0x00; //Setting PORTC's output to zero
PORTD= 0x02; //Setting PORTD.2's output to one
while (1)
{
PORTD= 0x02; //Turning on the PORTD LED
toggle(); //Toggling the PORTB's LEDs
PORTD= 0x00; //Turning off the PORTD LED
toggle(); //Toggling the PORTB's LEDs
}
}

Analyze your code
look at this section
while(PINA != 0x00){
t = t + 1;
_delay_ms(1);
if(t == 100) break;
}
while(t != 100){
//your algorithm body processed here
}
if one of switches (which connected to PORTA) is left high then the first while loop condition PINA != 0x00 is always true the loop finish only when t==100, then, when you go to the second while Loop it will not excite because the condition t != 100 is already false
in other words your toggle() function will not process any data until you release all switches
solution
I have made a simple code for what you try to do
int main(void)
{
DDRA = 0x00; //Setting PORTA as input
DDRB = 0xFF; //Setting PORTB as output
DDRC = 0xFF; //Setting PORTC as output
DDRD = 0x02; //Setting PORTD.2 as output
PORTB= 0x00; //Setting PORTB's output to zero
PORTC= 0x00; //Setting PORTC's output to zero
PORTD= 0x02; //Setting PORTD.2's output to one
unsigned char currentInput=PINA;
unsigned char previousInput=PINA;
unsigned char changedBitsMusk = 0;
while (1)
{
currentInput = PINA; //update current reading
changedBitsMusk = currentInput ^ previousInput; // calculate what is changed from previous reading
if (changedBitsMusk) // if there is change happen
{
if (currentInput&changedBitsMusk) // only process the data if change is HIgh (i.e only when button is pressed)
{
PORTB^=changedBitsMusk; // toggle the corresponding pins
}
}
previousInput = currentInput;
}
}

When you are trying to control outputs according to another port inputs, you can simply do something like
PORTB = PINA;
Of course, if you don't need the full port you can always mask the bits you need from the entire port.
Also, this is not the way to do it if you don't have the same pin numbers paired in both ports (i.e.: if the pin 3 of one port controls the behavior of the pint 6 of the other, for example)
This way you can avoid a lot of ugly and complex logic
If you want to continue in the path you are going, try removing the "else" from the "else if"s, they are not needed and as far as I can see, the conditions are mutually exclusive

Related

How to check if a port (input) is high? PIC16 C program

I cannot understand why my code won't work. It works when I set the condition PORTA == 0x00 but not when PORTA == 0x01. How do you check if a bit is high? Below is my code and my schematic (Crystal frequency is 4MHz).
#include<xc.h>
void main(){
int cnt;
int delay_cnt;
TRISA = 1; // PortA as input
TRISB = 0; // PortB as output
PORTB = 0x00; // Initialize LED as off
for(;;){ // Infinite loop
if(PORTA == 0x01){
for(cnt=0;cnt<3;cnt++){
PORTB = 0x01; // Turns on LED
for(delay_cnt=0;delay_cnt<10000;delay_cnt++);
PORTB = 0x00; // Turns off LED
for(delay_cnt=0;delay_cnt<10000;delay_cnt++);
}
}
}
}
In this case if (PORTA == 0x01), you are checking the whole port (8 bit).
If you want to check just bit 0 from the port use this:
if (PORTAbits.RA0 == 1){
or
if (PORTA & 0x01){

How to read multiple ADC channels using PIC24; can only get AN0

I am using a PIC24 to read data using 3 analog inputs but am only getting 1 to show the right result. I looked everywhere on the internet and am still not able to get the code to work.
I am trying to read 3 analog input signals and am only able to read in AN0.
I am using an accelerometer to obtain the data and show it on the LCD screen for now. I was able to implement 3 different ways to take the data and display it but only an0 works and an1 is not the right value.
void InitADC(int amask) {
AD1PCFG = 0xFFF8; // select AN0, AN1, AN2 as analog inputs
AD1CON1 = 0x00E0; // auto convert # end of sampling, Integer Data out.
// see Text pg. 179 & Sec. 17 on AD1CON1.
//AD1CON2bits.CSCNA = 1;
AD1CON3 = 0x1F01; // Tad = 2xTcy = 125ns. 31*Tad for conversion time.
//AD1CSSL = 0xFFF7; // Scan 3 channels
AD1CON1bits.ADON = 1; // Turn on the ADC
} // InitADC
main() {
InitADC(0xFFF8); // initialize the ADC and analog inputs
char x_string [12];
char y_string [12];
char z_string [12];
//TRISB = 1; // all PORTB pins as outputs
TRISBbits.TRISB0 = 1;
TRISBbits.TRISB1 = 1;
TRISBbits.TRISB2 = 1;
InitPMP(); // Initialize the Parallel Master Port
InitLCD(); // Initialize the LCD
float x_val;
float y_val;
float z_val;
float x_axis, y_axis, z_axis;
while (1) // main loop
{
x_axis= SelectPort(0);
x_val= ((((x_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(x_string, "X: %0.2f ", x_val);
ms_delay(2.5);
y_axis= SelectPort(1);
y_val= ((((y_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(y_string, "Y: %0.2f ", y_val);
ms_delay(2.5);
z_axis= SelectPort(2);
z_val= ((((z_axis * 3.3)/ 1024)-1.58)/0.380);
sprintf(z_string, "Z: %0.2f ", z_val);
ms_delay(2.5);
}
Here is the code where the data is read:
int SelectPort(int ch)
{
//int *result;
AD1CON1bits.ADON = 0; // Turn off the ADC to reconfigure
//result = &ADC1BUF0;
switch(ch) // set values based on the channel to use
{
case 0: // select AN0 as analog input
//AD1CHSbits.CH0SA=0;
//result = ADC1BUF0;
AD1PCFG = 0xFFFE;
break;
case 1:
//AD1CHSbits.CH0SA=1;
//result = ADC1BUF1;
AD1PCFG = 0xFFFD; // select AN1 as analog input
break;
case 2:
//AD1CHSbits.CH0SA=2;
AD1PCFG = 0xFFFB; // select AN2 as analog input
break;
// there's only so many options here, so there's not really a default case
}
AD1CON1bits.ADON = 1; // Turn on the ADC
AD1CHS = ch; // 1. select analog input channel
AD1CON1bits.SAMP = 1; // 2. Start sampling.
while (!AD1CON1bits.DONE); //5. wait for conversion to complete
AD1CON1bits.DONE = 0; // 6. clear flag. We are responsible see text.
return ADC1BUF0; // 7. read the conversion results
}
I am new to PIC24 and need help to figure out why I am not able to get multiple ADC channels to read the data.
You should always give some time for the ADC input voltage to settle after changing its input port. Also, make sure the impedance of your inputs signals are less than 10K.
Separating the ADC channel switching and reading its value will help, and give you an opportunity to add a delay.
I could not find AD1PCFG in the PIC24 datasheet. AD1PCFG is a PIC32 register... PIC24 uses ANSx and TRSx to set pins as analog inputs. Analog inputs should be set once, during the boot sequence. Feeding an analog signal to a digital input CMOS pin will result in increased power draw, and can even lead to hardware failure!
// Set the pins as analog inputs once and for all in your setup code.
void InitADC()
{
// select RB0/AN0, RB1/AN1, RB2/AN2 as analog inputs
TRISB |= 0x07;
ANSB |= 0x07;
// ...
}
void ADC_SelectInput(int ch)
{
AD1CHS = ch & 0x0F
}
int ADC_Read()
{
AD1CON1bits.SAMP = 1;
while (!AD1CON1bits.DONE)
;
AD1CON1bits.DONE = 0;
return ADC1BUF0;
}
Your loop then becomes:
// ...
int adc_in;
while (1)
{
ADC_SelectInput(0);
ms_delay(3); // settling delay, you could also do something
// else in the meantime instead of waiting.
// NOTE: the argument to ms_delay() is an integer
// so waiting at least 2.5 ms makes it 3.
// when computing floats, use float constants, not double.
x_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(x_string, "X: %0.2f ", x_val);
ADC_SelectInput(1);
ms_delay(3);
y_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(y_string, "Y: %0.2f ", y_val);
ADC_SelectInput(2);
ms_delay(3);
z_val = (((ADC_Read() * 3.3f) / 1024) - 1.58f) / 0.380f;
sprintf(z_string, "Z: %0.2f ", z_val);
}

Issue while reading data from I2c Slave device with PIC16F886

I am newbie to Pic Programming, I am using MPLAb & Hitech compiler to execute above code. I am trying to Interface PIC16F886 with ISL12022M Real time I2C device. i copied code example written for DS1307 interface with 16F887A PIC. I have capable to inteface Basic functionality with above . In below code While write into ISL12022M o could able to see data what i have send in memory register But as when Trying to read rtc time i could able to read last memory write value From SSPBUF. let me know any error in below code.
once I2c read value should be displayed on 4 digit seven segment display.
I think I am doing Misatake in this part. while Reading data i m just sending address so whatever last written in address it displaying.
#include <htc.h>
#include <stdio.h>
#include<pic.h>
#include<stdint.h>
#define _XTAL_FREQ 40000000
unsigned int i=0;
unsigned int k=0;
unsigned int count;
#define Pulse RA5
#define LED RC0
#define LED1 RC2
#define CONTROLREG 0xFF
#define SDA RC4 // Data pin for i2c
#define SCK RC3 // Clock pin for i2c
#define SDA_DIR TRISC4 // Data pin direction
#define SCK_DIR TRISC3 // Clock pin direction
#define DP RA4
#define I2C_SPEED 100 // kbps
unsigned short int cnt, num,Dgt=0;;
unsigned short int temp1,temp2,temp3;
unsigned short sec;
unsigned short min;
unsigned short hour;
unsigned short date;
unsigned short month;
unsigned short year;
unsigned short day;
unsigned short int temp=0;
unsigned short r_data;
#define Seg1 0x01
#define Seg2 0x02
#define Seg3 0x04
#define Seg4 0x08
void SetSeg(unsigned short data, unsigned short segno)
{
switch(data)
{
case 0: PORTB = 0x3F; break;
case 1: PORTB = 0x06; break;
case 2: PORTB = 0x5B; break;
case 3: PORTB = 0x4F; break;
case 4: PORTB = 0x66; break;
case 5: PORTB = 0x6D; break;
case 6: PORTB = 0x7D; break;
case 7: PORTB = 0x07; break;
case 8: PORTB = 0x7F; break;
case 9: PORTB = 0x6F; break;
default : PORTB = 0X00; break;
}
if(segno==1)
{
PORTA = Seg4;
}
if(segno==2)
{
PORTA = Seg3;
}
if(segno==3)
{
PORTA = Seg2;
}
if(segno==4)
{
PORTA = Seg1;
}
}
void Delay(int k)
{
int j;
for(j=0;j<k;j++);
}
void InitI2C(void)
{
SDA_DIR = 1; // Make SDA and
SCK_DIR =0; // SCK pins input
SSPCON = 0b00111000; //enables port for i2c
SSPCON2 = 0b00000000;
SSPADD = 10; // 100KHz = 8MHz/4(SSPADD+1)
// SSPSTAT = 0b11000000; // Slew rate disabled
}
void i2c_waitForIdle(void)
{
unsigned int i2ctimeout;
while(1)
{
i2ctimeout++;
if(i2ctimeout > 10)
{
i2ctimeout = 0;
return;
}
}
}
void I2C_Start(void)
{
SEN = 1; // Send start bit
i2c_waitForIdle();
/* while(!SSPIF); // Wait for it to complete
SSPIF = 0; // Clear the flag bit*/
}
void I2C_ReStart(void)
{
RSEN = 1; // Send Restart bit
i2c_waitForIdle();
/* while(!SSPIF); // Wait for it to complete
SSPIF = 0; // Clear the flag bit
while(RSEN==1);*/
}
void I2C_Stop(void)
{
PEN = 1; // Send stop bit
i2c_waitForIdle();
}
void I2C_Send_ACK(void)
{
ACKDT = 0; // 0 means ACK
ACKEN = 1; // Send ACKDT value
i2c_waitForIdle();
}
void I2C_Send_NACK(void)
{
ACKDT = 1; // 1 means NACK
ACKEN = 1; // Send ACKDT value
i2c_waitForIdle();
}
unsigned char I2C_Write( unsigned char i2cWriteData )
{
i2c_waitForIdle();
SSPBUF = i2cWriteData;
return (!ACKSTAT); // function returns '1'
}
int I2C_Read( unsigned char ack )
{
unsigned char i2cReadData;
//unsigned int i2cReadData;
i2c_waitForIdle();
RCEN = 1;
SDA=1;
SCK=1;
i2c_waitForIdle();
i2cReadData = SSPBUF;
SCK=0;
i2c_waitForIdle();
SCK=1;
if(ack)
{
ACKDT = 0;
}
else
{
ACKDT = 1;
}
ACKEN = 1; // send acknowledge sequence
return( i2cReadData );
}
unsigned int bcdtodecimal(unsigned int bcd)
{
unsigned int decimal;
decimal = (((bcd & 0xF0) >> 4) * 10) + (bcd & 0x0F);
return decimal;
}
void Init_ISL12022M(void)
{
I2C_Start(); // Start I2C communication
I2C_Write(0XD0); //Write Device Address
I2C_Write(0X08); //
I2C_Write(0X41); // Write 0x00 to Control register to disable SQW-Out
I2C_Stop(); // Stop I2C communication after initilizing
}
unsigned int Write_ISL12022M(unsigned short address, unsigned short w_data)
{
I2C_Start(); // Start I2C communication
I2C_Write(0XD0);
I2C_Write(address); //write address to write data
I2C_Write(w_data); //write data into hexadecimal
I2C_Stop();//stop I2C communication
return(w_data);
}
unsigned short Read_ISL12022M(unsigned short address)
{
I2C_Start();
I2C_Write(address); //address 0x68 followed by direction bit (0 for write, 1 for read) 0x68 followed by 0 --> 0xD0
I2C_Write(address);
I2C_ReStart();
I2C_Write(0xD1); //0x68 followed by 1 --> 0xD1
r_data=I2C_Read(0);
I2C_Stop();
return(r_data);
}
void SetDateTime()
{
I2C_Start();
I2C_Write(0xD0);
I2C_Write(0x00);
sec= Write_ISL12022M(0X00, 12); //01 sec
min = Write_ISL12022M(0X01,52); //01 sec
hour = Write_ISL12022M(0X02,9); //01 sec
day= Write_ISL12022M(0X03,7); //01 sec
date = Write_ISL12022M(0X04, 29); //01 sec
month =Write_ISL12022M(0X05,07); //01 sec
year = Write_ISL12022M(0X06,17); //01 sec
I2C_Stop();
}
void RTC_GetDateTime()
{
I2C_Start(); // Start I2C communication
I2C_Send_ACK();
sec = I2C_Read(1); // read second and return Positive ACK
I2C_Send_ACK();
min = I2C_Read(1); // read minute and return Positive ACK
I2C_Send_ACK();
hour= I2C_Read(0); // read hour and return Negative/No ACK
I2C_Send_ACK();
day = I2C_Read(1); // read weekDay and return Positive ACK
I2C_Send_ACK();
date= I2C_Read(1); // read Date and return Positive ACK
I2C_Send_ACK();
month=I2C_Read(1); // read Month and return Positive ACK
I2C_Send_ACK();
year =I2C_Read(0); // read Year and return Negative/No ACK
I2C_Send_ACK();
I2C_Stop(); // Stop I2C communication after reading the Date
}
void interrupt isr(void)
{
if(TMR1IF==1)
{
TMR1H=0xF6; // Load the time value(0xBDC) for 100ms delay
TMR1L=0x18; //Timer1 Interrupt for 65000
TMR1IF=0; // Clear timer interrupt flag
Dgt++;
if(Dgt>=5)
{
Dgt=0;
LED=!LED;
}
}
}
void Timer1_Interrupt()
{
INTCON = 0b00000000;
PIE1=0b00000001;
PIR1=0x01;
TMR1H=0x0B;
TMR1L=0xDC;
T1CON=0x31;
}
void Init_Controller()
{
cnt=100;
TRISC=0b01000000; // Intialize INput & output pheripherals
TRISB=0b10000000;
PORTB = 0b00000000;
TRISA=0b0000000;
ADCON0 = 0b00000000;
ANSEL = 0b00000000;
Timer1_Interrupt();
}
void main(void)
{
Init_Controller();
/* GIE=1;
PEIE=1;
TMR1IE=1; */
InitI2C();
Init_ISL12022M();
SetDateTime();
while(1)
{
RTC_GetDateTime();
SetSeg(year/ 10,2);
SetSeg(year%10,1);
}
}
The lines like:
I2C_Write(0XD0); //Write Device Address
are not a valid device addresses. Use 0xDE (or 0xAE for User SRAM)
From the datasheet:
Following a start condition, the master must output a Slave Address Byte. The 7 MSBs are the device identifiers. These bits are “1101111” for the RTC registers and “1010111” for the User SRAM.

ATMega2560 PCINT Interrupts

i am new to Atmel, so maybe the question is quite simple.
I have the following situation:
I have an ATMega2560 and want to get Interrupts on the Pins PK0-PK7. I am interested in the PIN Change from Low to HIGH. (I have connected one photocell to every PIN)
I have read that the interrupt for PCINT[0-2] are fired everytime (pin high and pin low) so i defined an array to ignore the PIN DOWN Interrupt.
So i have the following code:
Note: I do not understand, why i need to set DDRC as input and not DDRK ?
#define HIGH 1
#define LOW 0
volatile uint8_t portbhistory = 0xFF;
uint8_t pinState[8];
void initSystem()
{
int i;
for(i = 0; i < sizeof(pinState) / sizeof(*pinState); i++)
{
pinState[8] = 0;
}
DDRB = 0xff; // set Port B as output
DDRC = 0; // WHY DDRC ?? PCINT == PK0-7, so DDRK ? // all Pins input
PORTC = 0xff; // same question ... // turn on pullup for every pin
PCICR |= _BV(PCIE2); // enable interrupt for PCIE2
PCMSK2 = 0xff; // Interrupt at all pins
sei(); // turn on interrupts
}
int main(void)
{
initSystem();
while(1)
{
}
}
ISR(PCINT2_vect)
{
int i;
uint8_t changedbits;
changedbits = PINC ^ portbhistory;
portbhistory = PINC;
/*
if(pinState[changedbits] == HIGH)
{
pinState[changedbits] = LOW;
return;
} else
{
pinState[changedbits] = HIGH;
}
*/
setDebugLED(1);
_delay_ms(30);
setDebugLED(0);
}
If i connect 5V to one of the PINS (PK0-7) the LED flashes instantly. But if i disconnect the 5V it takes ~2seconds for the LED to flash again. In this time the one pin i connected 5V before does not let the LED flash again if i connect the 5 V again.
Other Ports work in this time.
So you could say, that the PIN HIGH interrupt for all pins work fine and get fired instantly but the PIN LOW interrupts need some time (~2sec.) In this time the port is "disabled" or somethig.
Can anyone help me with that?
EDIT
Just some wrong edit ...
EDIT 2
So, i totally forgot to post my solution here. sorry for that!
The solution is: My external circuit pulls the PIN to Ground or to 5V.
Thank you!

pwm value not changing

I have written a pwm code for Atmega128. I am using fast pwm mode with non-inverting pulse on compare match and I need to change the OCR0 value at certain times. Yet it doesn't change. Anyone knows what is the problem here ??
#include <avr/interrupt.h>
#include <avr/io.h>
uint8_t tick_1sec;
void timer1_init(void) // 1 second timer
{
OCR1A = 15624;
TIMSK |= (1<<OCIE1A);
TCCR1B = (1<<WGM12); //CTC mode
TCCR1B |= (1<<CS12)|(0<<CS11)|(1<<CS10);
}
ISR(TIMER1_COMPA_vect) //1 second interrupt
{
cli();
tick_1sec = 1;
sei();
}
void timer0_init(void) // fast pwm with OC0 non-inverting mode
{
TCCR0 = (1<<FOC0)|(1<<WGM01)|(1<<WGM00);
TCCR0 |= (1<<COM01)|(0<<COM00);
TCCR0 |= (1<<CS02)|(1<<CS01)|(1<<CS00);
OCR0 = 63;
TIMSK |= (1<<OCIE0);
}
int main(void)
{
uint8_t t = 0;
DDRB = 0xFF;
timer0_init();
timer1_init();
sei();
while(1){
if (tick_1sec)
{
tick_1sec = 0;
t++;
if (t == 10){
OCR0 = 127;
}
else if (t == 20){
OCR0 = 191;
}
else if (t == 30){
OCR0 = 63;
t = 0;
}
}
}
return 0;
}
Things to check:
I recommend declaring tick_1sec as volatile to prevent the compiler of hyper-optimizing that register.
What is your clock frequency? Your ISR will deliver 1s calls only if your CPU frequency is 16MHz (==> 16.000.000 / 1024 / 15624)
You might have a LED in your hardware which you can invert from a) the ISR b) within the first if () in main to see if this is ever reached.
update: "volatile"
The link provided by #skyrift in his comment is very worth reading.
When you use Atmel Studio, compile your code once with/without the volatile keyword and compare what the compiler is doing ==> Solution explorer / Output Files / *.lss ... you will see each C statement and how the compiler converts it to machine code ... an exercise worth once in a while when working with micros ...

Resources