I am sending a sequence as shown bellow MSB first order,using SPI.
When i look at the sequence displayed on osciloscope shown bellow, i see 111 from the left end and 11 from the right end it doesnt match the sequence i am sending .
the attached code shown bellow.
Where did i go wrong?
Thanks.
uint8_t Tx_WRSR[TX_WRSR_BUFFER_SIZE] = {0x01,0x2F,0xEF,0xD8}; //write status register
USART1->IEN = USART_IEN_TXC; //enable TXC interrupt flag
while(1)
{
Delay(1);
GPIO_PinOutClear(gpioPortA,4);
USART_Tx(USART1, Tx_WRSR[0]);
while( !(USART1->STATUS & USART_STATUS_TXC) ); //wait TILL TXC goes high
USART_Tx(USART1, Tx_WRSR[1]);
while( !(USART1->STATUS & USART_STATUS_TXC) ); //wait TILL TXC goes high
USART_Tx(USART1, Tx_WRSR[2]);
while( !(USART1->STATUS & USART_STATUS_TXC) ); //wait TILL TXC goes high
USART_Tx(USART1, Tx_WRSR[3]);
while( !(USART1->STATUS & USART_STATUS_TXC) ); //wait TILL TXC goes high
GPIO_PinOutSet(gpioPortA,4);
Delay(1);
}
}
Related
I'm working on a Distance measurement program using an AVR microcontroller. I use a 16x2 LCD and an ultrasonic sensor along with ATMEGA32A. I wrote a code to display the distance from the Ultrasonic HC-SR04 on the LCD screen, but it gives me false readings, it increases the distance when the object is very near and vice versa. I just want an accurate reading.
Ultrasonic datasheet
ATMEGA32A Datasheet
#include <avr/io.h>
#include <avr/interrupt.h>
#include <MrLcd/MrLCDmega32.h>
#define F_CPU 1000000
#include <util/delay.h>
#include <stdlib.h>
#define Trigger_pin PD0 /* Trigger pin */
static volatile int pulse = 0;
static volatile int i = 0;
int main(void)
{
Initialise();
DDRD = 0b11111011;
_delay_ms(50);
GICR |= 1<<INT0;
MCUCR |= 1<<ISC00;
int16_t count_a = 0;
char show_a[16];
sei();
while(1)
{
PORTD |= (1<<Trigger_pin);
_delay_us(10);
PORTD &= ~(1<<Trigger_pin);
count_a = pulse/58;
Send_A_String("Distance Sensor");
GoToMrLCDLocation(1,2);
Send_A_String("Distance=");
itoa(count_a,show_a,10);
Send_A_String(show_a);
Send_A_String(" ");
GoToMrLCDLocation(13,2);
Send_A_String("cm");
GoToMrLCDLocation(1,1);
}
}
ISR(INT0_vect)
{
if(i == 1)
{
TCCR1B = 0;
pulse = TCNT1;
TCNT1 = 0;
i = 0;
}
if(i==0)
{
TCCR1B |= 1<<CS10;
i = 1;
}
}
I tried to change the trigger pin definition and define it in the code itself but still no progress.
Update: I changed a bit more in the code but I'm getting hex values when the distance is more than 9, for example, 10 is being displayed as 1e.
This is for initialise function
void Initialise(void)
{
DataDir_MrLCDsControl|=1<<LightSwitch|1<<ReadWrite|1<<BipolarMood; //these information will go towards the LCD
_delay_ms(15); // Wait for the LCD to start
Send_A_Command(0x01); // to clear the screen
_delay_ms(2);
Send_A_Command(0x38); // TO tell LCD about 8 data lines
_delay_us(50);
Send_A_Command(0b00001110); //Some cursor command
_delay_us(50);
}
You are sending pulses at a very rapid rate (determined solely by the display update time), and they are asynchronous to the time/counter reset. You have no idea which pulse triggered the interrupt and it did not start at the same time as the timer.
I would suggest that you reset the counter at the start of the pulse, and capture the counter value on interrupt. When the time has exceeded the maximum range, send a new pulse:
First define some constants:
#define PULSES_PER_CMx100 (F_CPU * 100 / 68600)
#define MAX_RANGE_CM 300
#define MAX_RANGE_COUNT ((MAX_RANGE_CM * PULSES_PER_CMx100) / 100)
Then your measure/display loop might look like:
pulse = 1 ; // dummy start
GICR &= ~(1<<INT0) ; // Disable INT0
for(;;)
{
// Ready for new measurement?...
if( pulse != 0 )
{
// Send pulse and reset timer
PORTD |= (1<<Trigger_pin) ;
pulse = 0 ;
TCNT1 = 0 ;
_delay_us(10);
PORTD &= ~(1<<Trigger_pin) ;
// Wait for echo pulse interrupt...
GIFR |= 1<<INTF0; // Clear INT0 pending flag
GICR |= 1<<INT0 ; // Enable INT0
}
else // When measurement available...
{
int distance_cm = pulse * 100 / PULSES_PER_CMx100 ;
// display distance
...
}
// If out of range, timeout, send a new pulse
if( TCNT1 > MAX_RANGE_COUNT )
{
// Force a new pulse to be triggered
pulse = 1 ;
}
}
And the ISR:
ISR(INT0_vect)
{
pulse = TCNT1; // Capture time on interrupt
GICR &= ~(1<<INT0) ; // Disable further interrupts
}
Now bear in mind that that method will take measurements as fast as possible and since you are displaying them for human reading, that is rather unnecessary. You might simply put a delay in the loop - making the pulse timeout unnecessary, or better you could take the mean of multiple measurements to get a more robust measurement, or use a moving average window, with outlier rejection.
I have the MSP430 configured as an i2c master device and it is talking with two slave devices: a battery fuel gauge (LTC2943) and a battery charger (LT8491 evaluation board). I noticed that when talking with the battery charger, a duplicate byte gets sent over the i2c bus (this is bad); let's call it a 'ghost' byte. I only see this ghost/duplicate byte when talking with the battery charger but never with the fuel gauge. Both of those slave devices are on the same bus and I'm performing the same operation on both of them (reading 2-bytes); see code below. After several days of frustration, I noticed a peculiar difference between the two devices…
I noticed that when I talk to the fuel gauge (i2c address 0x64), the i2c clock line (SCL) idles low. But when I talk to the battery charger (i2c address 0x10) the SCL line idles high. Notice the SCL line behavior after the address byte in the provided pictures (links below). Per the MSP430 datasheet, it is my understanding that the SCL line should be idling LOW after the slave the address is pushed on the line (see page 833 of the MSP430 family guide here: www.ti.com/.../slau367p.pdf). There's a comment that says: "Bus stalled (SCL held low) until data available".
A few questions related to this:
Why does the SCL line idle differently on those two devices (see picture links below) even though they are on the same i2c bus and we are performing the same 2-byte read operation and using the same i2c driver code?
The MSP430 family guide (page 833) has that comment that says: "Bus stalled (SCL held low) until data available". Whose responsibility it is to hold the SCL low in this case? Is it the master (MSP430) or the slave device?
In the same page of the family guide (page 833), there are cases where it appears that a certain action needs to be performed DURING the transmission of a data byte, is this a hard requirement? For example, let's consider the case where we want to write one byte to the slave device, then follow it with a repeated start and then read one byte. The timing diagram on page 833 implies that we need to set UCTR=0 and UCTXSTT=1 DURING the transmission of the current data byte. What would happen if we set these registers AFTER the data byte has been transmitted?
The 'ghost' byte that I am seeing when talking with the battery charger is basically the MSP430 pushing the contents of the TXBUF out on the bus again! This occurs even after we have already configured the the MSP430 to be in receive mode. Please notice how/when/where I am toggling P4.6 in code as well as in the logic captures (see pictures). Why does this ghost byte happen?! Why does the MSP430 push it's TXBUF out on the line again?
Zoomed out picture of i2c communication:
Zoomed in picture with fuel gauge (good communication):
Zoomed in picture with battery charger (bad communication):
My code:
#include <string.h>
#include "driverlib.h"
#define BATTERY_CHARGER_I2C_ADDR ( 0x10 )
#define FUEL_GAUGE_I2C_ADDR ( 0x64 )
#define GENERAL_I2C_TIMEOUT_MS ( 3 ) //When waiting for an i2c flag/condition, timeout after 3 milliseconds so we don't get stuck waiting forever
//local (static) functions
static void init_hardware( void );
static bool hwm_i2c_master_receive_data( uint8_t slave_addr, uint8_t* read_ptr, uint8_t size_bytes );
static bool hwm_i2c_master_send_data( uint8_t slave_addr, uint8_t* write_ptr, uint8_t size_bytes, bool issue_stop );
void main (void)
{
uint8_t write_data;
uint8_t read_data[2];
//Initialize HWM manager
init_hardware();
while(1)
{
__delay_cycles( 1000000 ); //delay for about 1 second (1 cycle ~ 1us)
//read 2 bytes from fuel gauge
write_data = 0x08;//address of voltage register
hwm_i2c_master_send_data( FUEL_GAUGE_I2C_ADDR, &write_data, 1, false );
hwm_i2c_master_receive_data(FUEL_GAUGE_I2C_ADDR, read_data, 2);
//read 2 bytes from battery charger
write_data = 0x28;//address of Rsense1 configuration register
hwm_i2c_master_send_data( BATTERY_CHARGER_I2C_ADDR, &write_data, 1, false );
hwm_i2c_master_receive_data( BATTERY_CHARGER_I2C_ADDR, read_data, 2);
}
} //main()
static void init_hardware( void )
{
Timer_A_initContinuousModeParam timerA_cont_param;
//Disable internal watchdog timer
WDTCTL = WDTPW | WDTHOLD;
//Set VLO to drive ACLK with a divider of 1 (i.e. run ACLK at 10KHz)
CS_initClockSignal( CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1 );
//This block of code basically initializes TimerA1 to run continuously at 1KHz
memset( &timerA_cont_param, 0, sizeof(timerA_cont_param) );
timerA_cont_param.clockSource = TIMER_A_CLOCKSOURCE_ACLK; //ACLK to drive TimerA1
timerA_cont_param.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10; //Divide ACLK by 10 in order to get 1KHz
timerA_cont_param.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE; //Disable TimerA1 overflow interrupt
timerA_cont_param.timerClear = TIMER_A_DO_CLEAR; //Clear/reset TimerA1 counter
timerA_cont_param.startTimer = true; //Start TimerA1 counter
Timer_A_initContinuousMode( TIMER_A1_BASE, &timerA_cont_param );
//Configure P4.6 as an output pin for debugging (timing purposes)
GPIO_setAsOutputPin( GPIO_PORT_P4, GPIO_PIN6 );
GPIO_setOutputLowOnPin( GPIO_PORT_P4, GPIO_PIN6 );
//This block initializes the i2c peripheral
//Configure pins P1.6 (SDA) and P1.7 (SCL) for I2C (secondary module functionality)
GPIO_setAsPeripheralModuleFunctionInputPin( GPIO_PORT_P1, ( GPIO_PIN6 | GPIO_PIN7 ), GPIO_SECONDARY_MODULE_FUNCTION );
PMM_unlockLPM5(); //Clear the LOCKLPM5 bit so the GPIO and i2c configuration takes effect
//Configure the I2C bus
EUSCI_B_I2C_initMasterParam i2c_master_init_param = {0};
i2c_master_init_param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK; //use SMCLK clock signal
i2c_master_init_param.i2cClk = CS_getSMCLK(); //Give SMCLK freq in Hz
i2c_master_init_param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_100KBPS; //100KBps datarate
i2c_master_init_param.byteCounterThreshold = 0; //Don't care because 'no auto stop'
i2c_master_init_param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP; //We will handle the stop bit manually
EUSCI_B_I2C_initMaster( EUSCI_B0_BASE, &i2c_master_init_param );
EUSCI_B_I2C_enable(EUSCI_B0_BASE); //Enable the I2C bus (i.e. pull it out of reset state)
}
static bool hwm_i2c_master_receive_data( uint8_t slave_addr, uint8_t* read_ptr, uint8_t size_bytes )
{
bool proceed;
proceed = true;
//Basic sanity checks on address and size
if( NULL == read_ptr || 0 == size_bytes )
{
return false;
}
//Set P4.6 high for debugging (see scope captures)
GPIO_setOutputHighOnPin( GPIO_PORT_P4, GPIO_PIN6 );
UCB0I2CSA = slave_addr; //Set slave address
UCB0CTLW0 &= ~UCTR; //Set I2C bus in receiver (read) mode
UCB0IFG &= ~UCNACKIFG; //Clear NACK interrupt flag (fresh start)
UCB0CTLW0 |= UCTXSTT; //Issue START condition
//Wait for START condition to complete
TA1R = 0;
while( proceed && (UCB0CTLW0 & UCTXSTT) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (!(UCB0CTLW0 & UCTXSTT));
//If size is one byte, request STOP condition now! This is time critical (but not sure why :/)
if( proceed && (1 == size_bytes) )
{
UCB0CTLW0 |= UCTXSTP;
}
//Check that we received ACK from slave
proceed = proceed && (!(UCB0IFG & UCNACKIFG));
//Loop through and pull the requested number for bytes from the RX buffer
while( proceed && (size_bytes > 0) )
{
//Wait for RX buffer to be ready/full
TA1R = 0;
while( (!(UCB0IFG & UCRXIFG0)) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (UCB0IFG & UCRXIFG0);
if( proceed )
{
*read_ptr = UCB0RXBUF; //Pull byte out of RX buffer
read_ptr++; //Increment pointer
size_bytes--; //Decrement number of bytes left to read
//If there's only one byte left to read, request STOP condition. This is time critical (again, not sure why)
if( 1 == size_bytes )
{
UCB0CTLW0 |= UCTXSTP;
}
}
}
//Wait for the STOP condition to complete
TA1R = 0;
while( (UCB0CTLW0 & UCTXSTP) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (!(UCB0CTLW0 & UCTXSTP));
if( !proceed )
{
//If we got here, it means something went bad (e.g. timed out or slave NACK'ed),
//let's request STOP to terminate communication
UCB0CTLW0 |= UCTXSTP;
//wait for stop to complete
TA1R = 0;
while( (UCB0CTLW0 & UCTXSTP) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
}
//Clear P4.6
GPIO_setOutputLowOnPin( GPIO_PORT_P4, GPIO_PIN6 );
return proceed;
} //hwm_i2c_master_receive_data()
static bool hwm_i2c_master_send_data( uint8_t slave_addr, uint8_t* write_ptr, uint8_t size_bytes, bool issue_stop )
{
bool proceed;
proceed = true;
//Basic sanity checks on address and size
if( NULL == write_ptr || 0 == size_bytes )
{
return false;
}
UCB0I2CSA = slave_addr; //Set slave address
UCB0CTLW0 |= UCTR; //Set I2C bus in transmit mode
UCB0IFG &= ~UCNACKIFG; //Clear NACK interrupt flag (fresh start)
UCB0CTLW0 |= UCTXSTT; //Issue START condition
//Wait for START condition to complete
TA1R = 0;
while( proceed && (UCB0CTLW0 & UCTXSTT) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (!(UCB0CTLW0 & UCTXSTT));
//At this point, we have just pushed the slave address over the i2c line.
//According to the MSP430 datasheet, the MSP430 would/should hold the
//SCL line low until we put something in the UCB0TXBUF.
//In other words, during this delay, the SCL should be held low. This is
//true when talking with the fuel gauge (LTC2943) but is NOT the case when
//talking with the battery charger! Why is that?!
__delay_cycles( 100 ); //delay of ~100us, please notice the SCL line (pictures) during this delay
//Wait for tx buffer to be ready/empty
TA1R = 0;
while( proceed && (!(UCB0IFG & UCTXIFG0)) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (UCB0IFG & UCTXIFG0);
//Check that we received ACK from slave
proceed = proceed && (!(UCB0IFG & UCNACKIFG));
//Loop through and send the data
while( proceed && (size_bytes > 0) )
{
//Set P4.6 high for debugging
GPIO_setOutputHighOnPin( GPIO_PORT_P4, GPIO_PIN6 );
//Place byte in tx buffer for transmission
UCB0TXBUF = *write_ptr;
//Wait for byte to flush out of tx buffer
TA1R = 0;
while( proceed && (!(UCB0IFG & UCTXIFG0)) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (UCB0IFG & UCTXIFG0);
//Check the ACK after every byte to make sure it went ok
proceed = proceed && (!(UCB0IFG & UCNACKIFG));
//Increment write pointer and decrement remaining size
write_ptr++;
size_bytes--;
}
//If caller requested a STOP condition to be sent
if( proceed && issue_stop )
{
//Issue STOP condition
UCB0CTLW0 |= UCTXSTP;
//Wait for STOP condition to go through
TA1R = 0;
while( (UCB0CTLW0 & UCTXSTP) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
proceed = proceed && (!(UCB0CTLW0 & UCTXSTP));
}
if( !proceed )
{
//If we got here, it means something went bad (e.g. timed out or slave NACK'ed),
//let's request STOP to terminate communication
UCB0CTLW0 |= UCTXSTP;
//wait for stop to complete
TA1R = 0;
while( (UCB0CTLW0 & UCTXSTP) && (TA1R < GENERAL_I2C_TIMEOUT_MS) );
}
GPIO_setOutputLowOnPin( GPIO_PORT_P4, GPIO_PIN6 );
return proceed;
} //hwm_i2c_master_send_data()
I'm trying to write a (fairly) basic bit of software for and Attiny204, which handles interrupts when a clock input is pulled high.
When I run the code in the debug simulator in Atmel Studio, and set the clock input high, there is no interrupt flag generated. The interrupts do trigger when I manually produce an interrupt flag.
I've tried using different pins, and even, the other port. I can't seem to get the simulator to produce the interrupt flag.
In the past, I have used the AtMega328P in the simulator with equivalent code, and it works fine.
ISR(PORTA_PORT_vect)
{
//In this function we must:
//1. Shift all data up
shiftUp();
//2. Get new 8th bit
bit8 = VPORTA.IN & (1 << 1);
//3. Set Data Output Pin to bit0
if(bit0 == 0)
VPORTA.OUT &= ~(1 << 3);
else
VPORTA.OUT |= (1 << 3);
//4. Calculate new dimValue and dimMilliseconds
calcDim();
calcDelay();
}
int main(void)
{
initVariables();
/*
Below this, we must set the Data Direction (DD) of each pin we assigned.
*/
//Below, set the ISC of the Zero Cross Pin and the Clock Pin to allow interrupts
PORTA_PIN0CTRL |= 0b00000001; //Zero Cross
//PORTA_PIN1CTRL = 0b00000000; //Data In
//PORTA_PIN2CTRL = 0b00000000; //Data Next
//PORTA_PIN3CTRL = 0b00000000; //Triac Control
PORTB_PIN0CTRL |= 0b00000001; //Clock
//VPORTB.INTFLAGS |= 0b00000001;
//Set Port direction.
VPORTA.DIR = 0x30;
VPORTB.DIR = 0x00;
/*
Below this, we must enable interrupts.
*/
sei();
/* Replace with your application code */
while (1)
{
}
}
Why are you writing to VPORTA and VPORTB? The Tiny204 doesn´t have this registers.
You enable interrupts on both edges for Pin 0 of Port A (PIN0CTRL bit 0 is set = BOTHEDGES) and you don´t clear the interrupt flag in your ISR of Port A. Please take a look at the data sheet:
The interrupt request remains active until the interrupt flag is cleared. See the peripheral's INTFLAGSregister for details on how to clear interrupt flags.
I am trying to read several PWM signals from an RC receiver into an ATMega 2560. I am having trouble understanding how the ICRn pin functions as it appears to be used for all three compare registers.
The RC PWM signal has a period of 20ms with a HIGH pulse of 2ms being a valid upper value and 1ms being a valid lower value. So the value will sweep from 1000us to 2000us. The period should begin at the rising edge of the pulse.
I have prescaled the 16MHz clock by 8 to have a 2MHz timer an thus should be able to measure the signal to 0.5us accuracy which is sufficient for my requirements.
Please note that I am having not problems with PWM output and this question is specifically about PWM input.
My code thus far is attached below. I know that I will have to use ICR3 and an ISR to measure the PWM values but I am unsure as to the best procedure for doing this. I also do not know how to check if the value measured is from PE3, PE4, or PE5. Is this code right and how do I get the value that I am looking for?
Any help would be greatly appreciated.
// Set pins as inputs
DDRE |= ( 0 << PE3 ) | ( 0 << PE4 ) | ( 0 << PE5 );
// Configure Timers for CTC mode
TCCR3A |= ( 1 << WGM31 ) | ( 1 << WGM30 ); // Set on compare match
TCCR3B |= ( 1 << WGM33 ) | ( 1 << WGM32 ) | ( 1 << CS31); // Set on compare match, prescale_clk/8
TCCR3B |= ( 1 << ICES5 ) // Use rising edge as trigger
// 16 bit register - set TOP value
OCR3A = 40000 - 1;
OCR3B = 40000 - 1;
OCR3C = 40000 - 1;
TIMSK3 |= ( 1 << ICIE3 );
I had forgotten to post my solution a few months ago so here it is...
I used a PPM receiver in the end so this code can easily edited to read a simple PWM.
In my header file I made a structure for a 6 channel receiver that I was using for my project. This can be changed as required for receivers with more or less channels.
#ifndef _PPM_H_
#define _PPM_H_
// Libraries included
#include <stdint.h>
#include <avr/interrupt.h>
struct orangeRX_ppm {
uint16_t ch[6];
};
volatile unsigned char ch_index;
struct orangeRX_ppm ppm;
/* Functions */
void ppm_input_init(void); // Initialise the PPM Input to CTC mode
ISR( TIMER5_CAPT_vect ); // Use ISR to handle CTC interrupt and decode PPM
#endif /* _PPM_H_ */
I then had the following in my .c file.
// Libraries included
#include <avr/io.h>
#include <stdint.h>
#include "ppm.h"
/* PPM INPUT
* ---
* ICP5 Pin48 on Arduino Mega
*/
void ppm_input_init(void)
{
DDRL |= ( 0 << PL1 ); // set ICP5 as an input
TCCR5A = 0x00; // none
TCCR5B = ( 1 << ICES5 ) | ( 1 << CS51); // use rising edge as trigger, prescale_clk/8
TIMSK5 = ( 1 << ICIE5 ); // allow input capture interrupts
// Clear timer 5
TCNT5H = 0x00;
TCNT5L = 0x00;
}
// Interrupt service routine for reading PPM values from the radio receiver.
ISR( TIMER5_CAPT_vect )
{
// Count duration of the high pulse
uint16_t high_cnt;
high_cnt = (unsigned int)ICR5L;
high_cnt += (unsigned int)ICR5H * 256;
/* If the duration is greater than 5000 counts then this is the end of the PPM signal
* and the next signal being addressed will be Ch0
*/
if ( high_cnt < 5000 )
{
// Added for security of the array
if ( ch_index > 5 )
{
ch_index = 5;
}
ppm.ch[ch_index] = high_cnt; // Write channel value to array
ch_index++; // increment channel index
}
else
{
ch_index = 0; // reset channel index
}
// Reset counter
TCNT5H = 0;
TCNT5L = 0;
TIFR5 = ( 1 << ICF5 ); // clear input capture flag
}
This code will use an trigger an ISR every time ICP5 goes from low to high. In this ISR the 16bit ICR5 register "ICR5H<<8|ICR5L" holds the number of pre-scaled clock pulses that have elapsed since the last change from low to high. This count is typically less than 2000 us. I have said that if the count is greater than 2500us (5000 counts) then the input is invalid and the next input should be ppm.ch[0].
I have attached an image of PPM as seen on my oscilloscope.
This method of reading PPM is quite efficient as we do not need to keep polling pins to check their logic level.
Don't forget to enable interrupts using the sei() command. Otherwise the ISR will never run.
Let's say you want to do the following (I'm not saying this will allow you to accurately measure the PWM signals but it might serve as example on how to set the registers)
Three timers running, which reset every 20 ms. This can be done by setting them in CTC mode for OCRnA: wgm3..0 = 0b0100.
//timer 1
TCCR4A = 0;
TCCR1B = (1<<CS11) | (1<<WGM12);
OCR1A = 40000 - 1;
//timer 3 (there's no ICP2)
TCCR3A = 0;
TCCR3B = (1<<CS31) | (1<<WGM32);
OCR3A = 40000 - 1;
//timer 4
TCCR4A = 0;
TCCR4B = (1<<CS41) | (1<<WGM42);
OCR4A = 40000 - 1;
Now connect each of the three pwm signals to their own ICPn pin (where n = timer). Check the datasheet for the locations of the different ICPn pins (i'm pretty sure it's not PE3, 4, 5)
Assuming the pwm signals start high at t=0 and go low after their high-time for the remainder of the period. You want to measure the high-time so we trigger an interrupt for each when a falling edge occurs on the ICPn pin.
bit ICESn in the TCCRnB register set to 0 will select the falling edge (this is already done in the previous code block).
To trigger the interrupts, set the corresponding interrupt enable bits:
TIMSK1 |= (1<<ICIE1);
TIMSK3 |= (1<<ICIE3);
TIMSK4 |= (1<<ICIE4);
sei();
Now each time an interrupt is triggered for ICn you can grab the ICRn register to see the time (in clockperiods/8) at which the falling edge occurred.
So i am having this project. The system will give priority to emergency vehicles when they get close to traffic lights and when they get close i want to change the state of the traffic lights from red to green but in correct sequence.
I have managed to pull it off but it doesnt work with a real car because the xbees (coordinator on the vehicle and end device on home-made traffic lights) take sometime to communicate which is not enough for the speed of a vehicle approaching traffic lights with lets say an average speed of 60km/h.
The system works like this. There is an arduino on the vehicle which has a GPS shield and an xbee set to COORDINATOR on it. In the program on the arduino it checks if the gps reads coordinates that are saved inside the arduino so it checks against real time coordinates and if they match, the xbee gets to send a message across to the corresponding xbees that are set as end-devices on the traffic lights which again setup is arduino + xbee.
The problem 1 is i need to make a faster connection between the xbees (COORDINATOR - END DEVICE)
Here is the arduino sketch for ARDUINO-GPS-COORDINATOR. Note: GPS Shield is from adafruit and i use their code plus some of mine.
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
//
// This code shows how to listen to the GPS module in an interrupt
// which allows the program to have more 'freedom' - just parse
// when a new NMEA sentence is available! Then access data when
// desired.
//
// Tested and works great with the Adafruit Ultimate GPS module
// using MTK33x9 chipset
// ------> http://www.adafruit.com/products/746
// Pick one up today at the Adafruit electronics shop
// and help support open source hardware & software! -ada
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11);
SoftwareSerial xbee(13,12);
// If using hardware serial (e.g. Arduino Mega), comment out the
// above SoftwareSerial line, and enable this line instead
// (you can change the Serial number to match your wiring):
//HardwareSerial mySerial = Serial1;
Adafruit_GPS GPS(&mySerial);
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set to 'true' if you want to debug and listen to the raw GPS sentences.
#define GPSECHO true
// this keeps track of whether we're using the interrupt
// off by default!
boolean usingInterrupt = false;
void useInterrupt(boolean); // Func prototype keeps Arduino 0023 happy
void setup()
{
Serial.begin(115200);
Serial.println("Adafruit GPS library basic test!");
xbee.begin(9600);
xbee.println("SoftwareSerial on coordinator working!");
GPS.begin(9600);
// uncomment this line to turn on RMC (recommended minimum) and GGA (fix data) including altitude
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
// uncomment this line to turn on only the "minimum recommended" data
//GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCONLY);
// For parsing data, we don't suggest using anything but either RMC only or RMC+GGA since
// the parser doesn't care about other sentences at this time
// Set the update rate
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ); // 1 Hz update rate
// For the parsing code to work nicely and have time to sort thru the data, and
// print it out we don't suggest using anything higher than 1 Hz
// Request updates on antenna status, comment out to keep quiet
GPS.sendCommand(PGCMD_ANTENNA);
// the nice thing about this code is you can have a timer0 interrupt go off
// every 1 millisecond, and read data from the GPS for you. that makes the
// loop code a heck of a lot easier!
useInterrupt(true);
delay(1000);
// Ask for firmware version
mySerial.println(PMTK_Q_RELEASE);
}
// Interrupt is called once a millisecond, looks for any new GPS data, and stores it
SIGNAL(TIMER0_COMPA_vect) {
char c = GPS.read();
// if you want to debug, this is a good time to do it!
#ifdef UDR0
if (GPSECHO)
if (c) UDR0 = c;
// writing direct to UDR0 is much much faster than Serial.print
// but only one character can be written at a time.
#endif
}
void useInterrupt(boolean v) {
if (v) {
// Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function above
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
// do not call the interrupt function COMPA anymore
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
// difference_ratio
float diff_ratio = 0.010;
// COORDINATES INDEX
float coord_lat = 23;
float coord_lon = 23;
uint32_t timer = millis();
void loop() // run over and over again
{
// in case you are not using the interrupt above, you'll
// need to 'hand query' the GPS, not suggested :(
if (! usingInterrupt) {
// read data from the GPS in the 'main loop'
char c = GPS.read();
// if you want to debug, this is a good time to do it!
if (GPSECHO)
if (c) Serial.print(c);
}
// if a sentence is received, we can check the checksum, parse it...
if (GPS.newNMEAreceived()) {
// a tricky thing here is if we print the NMEA sentence, or data
// we end up not listening and catching other sentences!
// so be very wary if using OUTPUT_ALLDATA and trytng to print out data
//Serial.println(GPS.lastNMEA()); // this also sets the newNMEAreceived() flag to false
if (!GPS.parse(GPS.lastNMEA())) // this also sets the newNMEAreceived() flag to false
return; // we can fail to parse a sentence in which case we should just wait for another
}
// if millis() or timer wraps around, we'll just reset it
if (timer > millis()) timer = millis();
// approximately every 2 seconds or so, print out the current stats
if (millis() - timer > 2000) {
timer = millis(); // reset the timer
Serial.print("\nTime: ");
Serial.print(GPS.hour, DEC); Serial.print(':');
Serial.print(GPS.minute, DEC); Serial.print(':');
Serial.print(GPS.seconds, DEC); Serial.print('.');
Serial.println(GPS.milliseconds);
Serial.print("Date: ");
Serial.print(GPS.day, DEC); Serial.print('/');
Serial.print(GPS.month, DEC); Serial.print("/20");
Serial.println(GPS.year, DEC);
Serial.print("Fix: "); Serial.print((int)GPS.fix);
Serial.print(" quality: "); Serial.println((int)GPS.fixquality);
if (GPS.fix) {
//Serial.print("Location: ");
//Serial.print(GPS.latitude, 4); Serial.print(GPS.lat);
//Serial.print(", ");
//Serial.print(GPS.longitude, 4); Serial.println(GPS.lon);
Serial.print("Location (in degrees, works with Google Maps): ");
Serial.print(GPS.latitudeDegrees, 4);
Serial.print(", ");
Serial.println(GPS.longitudeDegrees, 4);
//Serial.print("Speed (knots): "); Serial.println(GPS.speed);
//Serial.print("Angle: "); Serial.println(GPS.angle);
//Serial.print("Altitude: "); Serial.println(GPS.altitude);
//Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
if(GPS.latitudeDegrees + diff_ratio >= coord_lat && coord_lat >= GPS.latitudeDegrees - diff_ratio) {
if(GPS.longitudeDegrees + diff_ratio >= coord_lon && coord_lon >= GPS.longitudeDegrees - diff_ratio){
Serial.println("location OKAY");
xbee.println("K");
}
}
//if((float)GPS.latitude > (home_lat - diff_ratio) && (float)
}
}
}
The important part is where it says if(GPS.fix()) and later on.
And here is the sketch for the traffic light simulation which if it receives the message "K" it will stay to green light until it does not receive it anymore.
#include <SoftwareSerial.h>
SoftwareSerial xbee(3,2);
int greenled = 8; //Led's and pins
int yellowled = 9;
int redled = 10;
int ard_led = 13;
void setup(){
pinMode(greenled,OUTPUT);
pinMode(yellowled, OUTPUT);
pinMode(redled, OUTPUT);
pinMode(ard_led,OUTPUT);
Serial.begin(9600);
xbee.begin(9600);
}
void loop(){
delay(700);
if(xbee.available() > 0 && xbee.read() == 'K' && digitalRead(ard_led) == 0){
//Serial.println("second block");
digitalWrite(redled,HIGH);
delay(1000);
digitalWrite(yellowled, HIGH); //Yellow and red on for 2 seconds
digitalWrite(ard_led,HIGH);
}else if(xbee.available() > 0 && xbee.read() == 'K' && digitalRead(ard_led) == 1){
//Serial.println("third block");
blinking_green();
}
else if(!xbee.available() && xbee.read() != 'K' && digitalRead(greenled) == 0){
//Serial.println("first block");
digitalWrite(redled, HIGH);
delay(1000);
digitalWrite(yellowled, HIGH); //Yellow and red on for 2 seconds
delay(1000);
digitalWrite(redled, LOW); //Red and Yellow off
digitalWrite(yellowled, LOW);
digitalWrite(greenled, HIGH); //Green on for 5 seconds
delay(3000);
digitalWrite(greenled, LOW); //Green off, yellow on for 2 seconds
digitalWrite(yellowled, HIGH);
delay(1000);
digitalWrite(yellowled,LOW);
digitalWrite(redled,HIGH);
} else if(!xbee.available() && xbee.read() != 'K' && digitalRead(greenled) == 1 && digitalRead(yellowled == 0)){
//Serial.println("fourth block");
digitalWrite(greenled,LOW);
digitalWrite(yellowled, HIGH);
delay(1000);
digitalWrite(yellowled, LOW);
digitalWrite(redled,HIGH);
digitalWrite(ard_led,LOW);
}
}
void blinking_green(){
digitalWrite(redled, LOW); //Red and Yellow off
digitalWrite(yellowled, LOW);
digitalWrite(greenled,HIGH);
delay(2500);
}
Problem 2: How can i interrupt the traffic lights simulation instantly when it receives a message from a nearby arduino to change the traffic light to green BEFORE it finishes that loop? Because in a real example green and red light would say for over 20 seconds.
Question: Will a faster baud rate on the xbees achieve faster xbee communication?
thank you in advance
You need to change the loop() on your traffic light simulation. Have a variable you use to store the "state" of the light, along with a timer to keep track of when the next state change happens. This way, your loop can also check the XBee serial input every time around.
if (xbee_event_happened()) {
set_leds_off();
timer = millis();
state = STATE_FLASH_GREEN_OFF;
}
switch (state) {
case STATE_FLASH_GREEN_OFF:
if (millis() - timer > 1000) {
set_leds_green();
state = STATE_FLASH_GREEN_ON;
timer = millis();
}
break;
case STATE_FLASH_GREEN_ON:
if (millis() - timer > 1000) {
set_leds_off();
state = STATE_FLASH_GREEN_OFF;
timer = millis();
}
break;
case STATE_RED:
if (millis() - timer > 5000) {
set_leds_green();
state = STATE_GREEN;
timer = millis();
}
break;
case STATE_GREEN:
if (millis() - timer > 3000) {
set_leds_yellow();
state = STATE_YELLOW;
timer = millis();
}
break;
// etc.
}
This just covers the basics, but it shows an important aspect of loop() function design -- it should never run for more than a few milliseconds. Don't have a delay inside of your main loop, track the state of the device and then use logic to decide if the state needs to change on that pass of the loop.
Additionally, use higher baud rates when possible to avoid latency from serial transmissions, get rid of the 700ms delay in your loop, and organize your if/else structure better:
if (xbee.available() > 0) {
character = xbee.read();
if (character == 'K') {
if (digitalRead(ard_led)) {
// second block
} else {
// third block
}
} else if (character == 'X') {
// do something different? Vehicle left area?
}
}