I also have a problem with DRDY. I need to include DRDY. The pins for DRDY are RD2 and RD5. They are both inputs.
Here is the information for DRDY.
DRDY Pin
DRDY is an open-drain output (in SPI mode) or bidirectional pin (in UART mode) with an internal 20 k – 50 k pullup
resistor.
Most communications failures are the result of failure to properly observe the DRDY timing.
Serial communications pacing is controlled by this pin. Use of DRDY is critical to successful communications with the
QT1481. In either UART or SPI mode, the host is permitted to perform a data transfer only when DRDY has returned
high. Additionally, in UART mode, the QT1481 delays responses to the host if DRDY is being held low by the host.
After each byte transfer, DRDY goes low after a short delay and remains low until the QT1481 is ready for another
transfer. A short delay occurs before DRDY is driven low because the QT1481 may otherwise be busy and requires
a finite time to respond.
DRDY may go low for a microsecond only. During the period from the end of one transfer until DRDY goes low and
back high again, the host should not perform another transfer. Therefore, before each byte transmission the host
should first check that DRDY is high again.
If the host wants to perform a byte transfer with the QT1481 it should behave as follows:
1. Wait at least 100 µs after the previous transfer (time S5 in Figure 3-2 on page 23: DRDY is guaranteed to go
low before this 100 µs expires).
2. Wait until DRDY is high (it may already be high).
3. Perform the next transfer with the QT1481.
In most cases it takes up to 3 ms for DRDY to return high again. However, this time is longer with some commands
or if the STS_DEBUG setup is enabled, as follows:
0x01 (Setups load): <20 ms
0x02 (Low Level Cal and Offset): <20 ms
Add 15 ms to the above times if the STS_DEBUG setup is enabled.
Other DRDY specifications:
Min time DRDY is low: 1 µs
Max time DRDY is low after reset: 100 ms
The timing diagram is this:
How can implement that?
The code I have written with my friend is written here:
#include <xc.h>
#include "PIC.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <pic18f45k80.h>
#define MSB 1
#define LSB 0
// SPI PIN CONFIGURATION
#define SCK_TRIS TRISCbits.TRISC3 = 0 ;
#define SDO_TRIS TRISCbits.TRISC5 = 0 ;
#define SDI_TRIS TRISCbits.TRISC4 = 1 ;
#define QTA_SS_TRIS TRISDbits.TRISD4 = 0 ;
#define QTB_SS_TRIS TRISEbits.TRISE2 = 0 ;
#define QTA_SS_LAT_LOW LATDbits.LATD4 = 0 ;
#define QTA_SS_LAT_HIGH LATDbits.LATD4 = 1 ;
#define QTB_SS_LAT_LOW LATEbits.LATE2 = 0 ;
#define QTB_SS_LAT_HIGH LATEbits.LATE2 = 1 ;
#define QTA_DRDY_TRIS TRISDbits.TRISD5 = 1 ;
#define QTB_DRDY_TRIS TRISDbits.TRISD2 = 1 ;
#define QTA_DRDY_LAT_LOW LATDbits.LATD5 = 0 ;
#define QTA_DRDY_LAT_HIGH LATDbits.LAT52 = 1 ;
#define QTB_DRDY_LAT_LOW LATDbits.LAT25 = 0 ;
#define QTB_DRDY_LAT_HIGH LATDbits.LATD2 = 1 ;
#define QTB_DRDY PORTDbits.RD2 ;
#define QTA_DRDY PORTDbits.RD5 ;
// FREQUENCY SELECT
#define _XTAL_FREQ 16000000
// PIN SETUP
void PIN_MANAGER_Initialize(void)
{
/**
LATx registers
*/
LATE = 0x00;
LATD = 0x00;
LATA = 0x00;
LATB = 0b00010000;
LATC = 0x00;
/**
TRISx registers
*/
TRISE = 0x00;
TRISA = 0x08;
TRISB = 0x01;
TRISC = 0b00010000;
TRISD = 0xEF;
PORTC = 0b00010010 ;
/**
ANSELx registers
*/
ANCON0 = 0x00;
ANCON1 = 0x00;
/**
WPUx registers
*/
WPUB = 0x00;
INTCON2bits.nRBPU = 1;
}
// SPI
void SPI_Initialize(void)
{
// SMP Middle; CKE Idle to Active;
SSPSTAT = 0b00000000;
// SSPEN enabled; WCOL no_collision; CKP Idle:High, Active:Low; SSPM FOSC/4; SSPOV no_overflow;
SSPCON1 = 0b00111010;
// SSPADD 0;
SSPADD = 0x00;
ADCON0 = 0 ;
ADCON1 = 0x0F ; //Makes all I/O digital
SCK_TRIS ;
SDO_TRIS ;
SDI_TRIS ;
QTA_SS_TRIS ;
QTB_SS_TRIS ;
QTA_DRDY_TRIS ;
QTB_DRDY_TRIS ;
}
signed char WriteSPI( unsigned char data_out )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clears BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPCON1bits.WCOL = 0; //Clear any previous write collision
SSPBUF = data_out; // write byte to SSPBUF register
if ( SSPCON1 & 0x80 ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
while( !PIR1bits.SSPIF ); // wait until bus cycle complete
return ( 0 ); // if WCOL bit is not set return non-negative#
}
unsigned char ReadSPI( void )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clear BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPBUF = 0x00; // initiate bus cycle
while(!PIR1bits.SSPIF); // wait until cycle complete
return ( SSPBUF ); // return with byte read
}
unsigned char DataRdySPI( void )
{
if ( SSPSTATbits.BF )
return ( +1 ); // data in SSPBUF register
else
return ( 0 ); // no data in SSPBUF register
}
// SOFTWARE EUART
void out_char(char character, char bit_order){
uint8_t i = 0;
RSOUT = 1 ; // MSB
__delay_ms(1);
RSOUT = 0 ; // START
__delay_us(100);
for (i = 8; i>0; --i){
if (bit_order){ // Bit order determines how you will put the bits, from left to right (MSB) or right to left (LSB)
RSOUT = (character & 0x80) ? 1:0; // in MSB you compare the left-most bit doing an AND with 0x80, and put 1 if true, 0 elsewhere.
character <<= 1; // Shift the character to the left, discrading the bit just sent
} else {
RSOUT = (character & 0x01); // in LSB you compare the right-most bit doing an AND with 0x01, and put 1 if true, 0 else.
character >>= 1; // Shift the character to the right, discrading the bit just sent
}
__delay_us(100);
}
RSOUT = 1 ; // STOP
}
void out_str(char * string, uint8_t len, char bit_order){
uint8_t i = 0;
for (i = 0; i< len; i++){
out_char(string[i], bit_order);
}
}
void SYSTEM_Initialize(void)
{
PIN_MANAGER_Initialize() ;
SPI_Initialize() ;
}
void main(void)
{
SYSTEM_Initialize() ;
while (1)
{
QTB_SS_LAT_LOW ; // Transmit data
char temp ;
WriteSPI(0x0F) ; // Send a byte
while(!DataRdySPI()) ; // wait for a data to arrive
temp = ReadSPI(); // Read a byte from the
QTB_SS_LAT_HIGH ; // Stop transmitting data
__delay_us(100) ;
}
}
No. Do not just write a bunch of code, then see what it does. That kind of shotgun (or, if you prefer, spaghetti-to-the-wall) approach is a waste of effort.
First, drop all those macros. Instead, write comments that describe the purpose of each chunk of code, like the first three assignments in your SPI_Initialize() function.
Next, convert your specification to pseudocode. The format does not matter much, just use something that lets you keep your mind focused on what the purpose is, rather than on the details on how to do it.
The datasheet says that with SPI, there are three outputs from the PIC (^SS, SCK, MOSI on the QT1481), and two inputs (^DRDY and MISO on the QT1481). I'll use those names for the data lines, and for their respective I/O pin names on the PIC.
The setup phase on the PIC should be simple:
Make ^DRDY an input
Make ^SS an output, set it HIGH
Make SCK an output, set it LOW
Make MOSI an output, set it LOW
Make MISO an input
Set up SPI using SCK, MOSI, MISO
Each transfer is a bidirectional one. Whenever you send data, you also receive data. The zero command is reserved for receiving multiple data, says the datasheet. So, you only need a function that sends a byte, and at the same time receives a byte:
Function SPITransfer(command):
Make sure at least 0.1ms has passed since the previous transfer.
Do:
Nothing
While (^DRDY is LOW)
Set ^SS LOW
response = Transfer(command)
Set ^SS HIGH
Return response
End Function
As I understand it, for PICs and properly initialized hardware SPI the response = Transfer(command) line is in C
SSPBUF = command;
while (!DataRdySPI())
;
response = SSPBUF;
You can also bit-bang it, in which case it is (in pseudocode):
response = 0
For bit = 7 down to 0, inclusive:
If (command & 128):
Set MOSI high
Else:
Set MOSI low
End If
Set SCK low
Sleep for a half period
command = command / 2
response = response * 2
If MISO high:
response = response + 1
End If
Set SCK high
Sleep for a half period
End For
but obviously the hardware SPI approach is better.
(When you get this working, you can use the hardware SPI without a wait loop from a timer interrupt, making the communications essentially transparent to the "main operation" of the PIC microcontroller. That requires a slightly different approach, with a command and response queues (of a few bytes), but will make it much easier for the PIC to do actual work, other than just scan the QT1481.)
After a reset, you essentially send 0x0F until you get 0xF0 back:
while (SPITransfer(0x0F) != 0xF0)
;
At this point, you have the steps you need to implement in C. OP also has the hardware (an oscilloscope) to verify their code works.
Related
Hi I'm using Watchdog to control LED lights. the microcontroller is connected to laptop with a cable. Input to the microcontroller is 5V. now on the pins there is one pin PB2 which is 5V directly connected to the input. I want to do that if I remove that 5V female-to-male wire from PB2 the LED turn off. when I plug in again with PB2 it the light turn on after that the watchdog called and turn red light off after every 4 sec
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#define WDTO_4S 8
void fun_red(){
PORTB.IN=0x04; // PB2 as input
PORTB.DIR=0x09; // PB0 and PB3 output
PORTB.OUTCLR=0x00; // Green
PORTB.OUTSET=0x01;
wdt_enable(WDTO_4S);
do
{
if (PORTB.IN)
{
PORTB.OUTCLR=0x00; // red
}
else{
PORTB.OUTSET=0x08;
}
} while(1);
wdt_reset();
}
int main(void)
{
fun_red();
}
OK, so in the edit it seems that you are wish to use PB2 as an input to control the LED on PB3 - is that correct?
You should not think in terms of voltages, these are digital I/O they have a high state and a low state and a threshold between the two. The inputs on your part will go high at anything above 0.8 volts. However removing the input voltage by disconnecting the wire will have no effect if you have enabled the internal pull-up, and if you have not, it will float and may not trigger a logic 0 (zero).
You either need an external pull-down resistor, or you enable the internal pull-up and invert the logic - i.e. LED on when the input is low, and LED off when input is high. Then instead of connecting PB2 to 5V you connect it to GND. Removing the GND connection will cause the logic state to become 1 because of the internal-pull up. Moreover this is much safer as it avoids any risk of applying excessive voltage to the input and damaging the processor.
Given your apparent lack of knowledge of electronics, I would ask also how you have connected the LED. You should have a current limiting series resistor and have the LED connected the correct way around.
It remains unclear what the purpose of the watchdog is in this. It serves no purpose, but if you enable it you must maintain it to prevent the processor resetting. The purpose of a watchdog is to reset the processor if the software stops running normally by resetting it regularly in the normal software execution path, so if the normal path stops executing the system restarts.
Start by reading this primer App Note: Getting Started with GPIO
First of all, you have misunderstood the function of the GPIO registers. PORTB.IN reads the input states of the PORTB pins that are configured as input. It is read-only so:
PORTB.IN=0x04; // PB2 as input
has no effect and certainly does not configure the pin as an input. That is done by DDR, so:
PORTB.DIR=0x09; // PB0 and PB3 output
sets PB0 and PB3 as outputs, and anything that is not an output is an input - so PB2 is an input by virtue of this line.
Now if you do as I recommend and use the internal pull-up for PB2, you must enable that in the PIN2CTRL register:
PORTB.PIN2CTRL = 0x08 ; // PULLUPEN
Incidentally there are symbols defined for all thse bits so you should be able to write:
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
which also makes the comment unnecessary.
The registers OUTSET and OUTCLR, set (logic 1/high) and clear (logic 0/low) respectively. The pins to be set/clear are determined by a bit mask passed in. As such:
PORTB.OUTCLR=0x00; // Green
does nothing, it sets no pins to the low state. To set the red LED (I am assuming PB3) off and the green LED (PB0) on:
PORTB.OUTCLR = PIN3_bm ;
PORTB.OUTSET = PIN0_bm ;
Now, your test of PORTB.IN as if it were Boolean will work in this case because you have only one input. But if you have more than one input, it will not distinguish between then, and it is a bad habit in any case to use an integer expression as if it were Boolean. You should explicitly test the state of PB2:
// If PB2 is low (GND wire connected)
if( (PORTB.IN & PIN2_bm) == 0 )
{
PORTB.OUTSET = PIN3_bm ; // red on
}
else
{
PORTB.OUTCLR = PIN3_bm ; // red off
}
The purpose of a watchdog timer is to reset the processor if the software fails to operate normally. You set the timeout, then you need to reset it regularly in the code to prevent a reset. It is not a general purpose you would use for a delay. For that you would use a hardware timer. Unfortunately it gets a little complex; for your board running at 24MHz, the maximum timer period for the 16 bit TIMER1 is about 2.8 seconds. For more flexibility you would normally implement a timer interrupt to count a number of smaller periods and count the number of times the timer reloads. For example:
volatile unsigned tick = 0 ;
ISR (TIMER1_OVF_vect) // Timer1 ISR
{
tick++ ;
}
void tickStart()
{
TCCR1B = (1<<CS11) // Prescaler 24MHz / 8
TCNT1 = 3000 ; // 1 ms at 24MHz/8
TCCR1A = 0x00;
TIMSK = (1 << TOIE1) ; // Enable timer1 overflow interrupt(TOIE1)
sei(); // Enable global interrupts
}
unsigned getTick()
{
unsigned t = 0 ;
do
{
t = tick ;
} while( tick != t ) ;
return t ;
}
Then for a 4 second delay while also monitoring P2 you might do:
// Wait 4 seconds or until PB2 disconnected
unsigned start = getTick() ;
while( getTick() - start < 4000 &&
(PORTB.IN & PIN2_bm) != 0 )
{
// waiting
}
Finally comment your code. It will allow you to understand what you are trying to do and and when you post a question it will tell others what you are trying to do and it it will make your question simpler by having the explanation in-line with the code so it is clear not only what you want the code to do, but how you think it is doing it.
Putting it all together, the following is more plausible:
void fun_red( void )
{
// Initialise I/O
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
PORTB.DIR = PIN0_bm | PIN3_bm ;
PORTB.OUTSET = PIN0_bm ; // Green on
for(;;)
{
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Wait for PB2 to be connected (to GND)
while( (PORTB.IN & PIN2_bm) == 0 )
{
// waiting
}
// Red on
PORTB.OUTSET = PIN3_bm ;
// Wait 4 seconds or until PB2 disconnected
unsigned start = getTick() ;
while( getTick() - start < 4000 &&
(PORTB.IN & PIN2_bm) != 0 )
{
// waiting
}
// Wait for PB2 to be reconnected
while( (PORTB.IN & PIN2_bm) != 0)
{
// waiting
}
}
}
int main( void )
{
tickStart() ;
fun_red() ;
}
Remember in this you connect PB2 to GND (0 volts) not not 5V/Vcc. The requirements in the question are not entirely clear but what this will do (untested - I don't have the hardware) is:
while PB2 is disconnected, the LED will be off,
when PB2 is connected, the LED will be lit for 4 seconds or until PB2 is reconnected.
While it is not its intended purpose and it is not really useful for general purpose, because this requirement is very simple it is possible to us the watchdog timer to implement the desired behaviour as follows:
void fun_red()
{
// Initialise I/O
PORTB.PIN2CTRL = PORT_PULLUPEN_bm ;
PORTB.DIR = PIN0_bm | PIN3_bm ;
// Green on
PORTB.OUTSET = PIN0_bm ;
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Enable watchdog
wdt_enable(WDTO_4S);
for(;;)
{
// Maintain watchdog while waiting for
// PB2 to be connected (to GND)
while( (PORTB.IN & PIN2_bm) == 0 )
{
wdt_reset() ;
}
// Red on
PORTB.OUTSET = PIN3_bm ;
// Maintain watchdog while waiting for
// PB2 to be disconnected (from GND)
while( (PORTB.IN & PIN2_bm) != 0)
{
wdt_reset() ;
}
// Red off
PORTB.OUTCLR = PIN3_bm ;
// Wait for PB2 to be reconnected (to GND)
// without maintaining watchdog. Will reset after 4 seconds
// if not reconnected.
while( (PORTB.IN & PIN2_bm) != 0)
{
// do nothing
}
}
}
Here after PB2 is disconnected, the watchdog is not maintained, so that after 4 seconds a reset will occur and it with restart the program and wait for PB2 to be connected. I think I should class such code as a "dirty-trick", not something to be considered normal or particularly useful.
I have to write a C code so that the RGB LED on the board breaths. My code is blinking not breathing. My teacher said that varying brightness is achieved by varying duty-cycle so in that case I can't use pwm. Please help me to understand this code.
#include <stdint.h>
#include <stdlib.h>
#define SYSCTL_RCGC2_R (*((volatile unsigned long *)0x400FE108))
#define SYSCTL_RCGC2_GPIOF 0x00000020 //port F clock gating control
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
#define GPIO_PORTF_DIR_R (*((volatile unsigned long *)0x40025400))
#define GPIO_PORTF_DEN_R (*((volatile unsigned long *)0x4002551C))
void delay (double sec);
int cond;
int main(void){
SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;
GPIO_PORTF_DIR_R=0x0E;
GPIO_PORTF_DEN_R=0x0E;
cond=0;
while(1){
GPIO_PORTF_DATA_R = 0x02;
delay(12.5);
GPIO_PORTF_DATA_R = 0x00;
delay(0);
GPIO_PORTF_DATA_R = 0x02;
delay(2.5);
GPIO_PORTF_DATA_R = 0x00;
delay(10);
GPIO_PORTF_DATA_R = 0x02;
delay(5);
GPIO_PORTF_DATA_R = 0x00;
delay(7.5);
GPIO_PORTF_DATA_R = 0x02;
delay(7.5);
GPIO_PORTF_DATA_R = 0x00;
delay(5);
GPIO_PORTF_DATA_R = 0x02;
delay(12.5);
GPIO_PORTF_DATA_R = 0x00;
delay(0);
GPIO_PORTF_DATA_R = 0x02;
delay(7.5);
GPIO_PORTF_DATA_R = 0x00;
delay(5);
GPIO_PORTF_DATA_R = 0x02;
delay(5);
GPIO_PORTF_DATA_R = 0x00;
delay(7.5);
}
return 0;
}
void delay(double sec){
int c=1, d=1;
for(c=1;c<=sec;c++)
for(d=1;d<= 4000000;d++){}
}
There are two ways you can drive LEDs: either with constant current through some general-purpose I/O, or with repeated duty cycle from PWM. PWM meaning pulse-width modulation and it will happen with pulses that are too fast for the human eye to notice, could be anywhere from some 100Hz up to 10kHz or so.
The main advantage of PWM is that you easily can control current. Which is case of RGB means color intensity of the 3 individual LEDs. Most smaller LEDs are rated at 20mA so that's usually the maximum current you are aiming for, corresponding to 100% duty cycle.
The correct way to achieve this is to use PWM.
But what your current code does is to "bit bang" simulate PWM by pulling GPIO pins. That's very crude and inefficient. Normally microcontrollers have a timer and/or PWM hardware peripheral built in, where you just provide a duty cycle and the hardware takes care of everything from there. In this case you would set up 3 PWM hardware channels which should ideally be clocked at the same time.
LEDs are diodes with different forward voltage depending on chemistry. So you very likely have different forward voltages per each of the 3 colors. You have to check the datasheet of the RGB and look for luminous intensity experessed in candela. In this case very likely millicandela, mcd. Lets assume that your green led has 300mcd but the red and blue have 100mcd. They are somewhat linear, or you can probably get away with assuming they are. So a crude equation in this case is to give the green LED 3 times less current than the others, in order to get an even mix of colors. Once you have compensated for that, you can give your 3 PWM channels a RGB code and hopefully get the corresponding color.
As a side note, the delay function in your code is completely broken in many ways. The loop iterator for such busy-delays must be volatile or any half-decent compiler will simply remove the delay when optimizations are enabled. And there is no reason to use floating point either.
If you are doing it with your delay function and your delay resolution is in seconds as suggested in the code of course it will "blink" - the frequency needs to be faster than human visual perception - say for example about 50Hz, then to get a smooth variation you might divide that up into say 20 levels, requiring a millisecond delay.
In any case your delay() function defeats itself by taking a floating point number of seconds but comparing it with an integer loop counter - it will only ever work in whole seconds.
So given a function delayms( unsigned millisec ) (which I discuss later) then:
#define BREATHE_UPDATE_MS 100
#define BREATHE_MINIMUM 0
#define PWM_PERIOD_MS 20
unsigned tick = 0 ;
unsigned duty_cycle = 0 ;
unsigned cycle_start_tick= 0 ;
unsigned breath_update_tick = 0 ;
int breathe_dir = 1 ;
for(;;)
{
// If in PWM "mark"...
if( tick - cycle_start_tick < duty_cycle )
{
// LED on
GPIO_PORTF_DATA_R |= 0x02 ;
}
// else PWM "space"
else
{
// LED off
GPIO_PORTF_DATA_R &= ~0x02 ;
}
// Update tick counter
tick++ ;
// If PWM cycle complete, restart
if( tick - cycle_start_tick >= PWM_PERIOD_MS )
{
cycle_start_tick = tick ;
}
// If time to update duty-cycle...
if( tick - breath_update_tick > BREATHE_UPDATE_MS )
{
breath_update_tick = tick ;
duty_cycle += breathe_dir ;
if( duty_cycle >= PWM_PERIOD_MS )
{
// Breathe in
breathe_dir = -1 ;
}
else if( duty_cycle == BREATHE_MINIMUM )
{
// Breathe out
breathe_dir = 1 ;
}
}
delayms( 1 ) ;
}
Change BREATHE_UPDATE_MS to breathe faster, change BREATHE_MINIMUM to "shallow breathe" - i.e. not dim to off.
If your delay function truly results in a delay resolution in seconds then approximately and rather crudely:
void delayms( unsigned millisec )
{
for( int c = 0; c < millisec; c++ )
{
for( volatile int d = 0; d < 4000; d++ ) {}
}
}
However that suggests to me a rather low core clock rate, so you may need to adjust that. Note the use of volatile to prevent the removal of the empty loop by the optimiser. The problem with this delay is that you will need to calibrate it to the clock speed of your target and its timing is likely to differ in any case depending on what compiler you use and what compiler options you use. It is generally a poor solution.
In practice using a "busy-loop" delay for this is ill-advised and crude and it would be better to use the Cortex-M SYSTICK:
volatile uint32_t tick = 0 ;
void SysTick_Handler(void)
{
tick++ ;
}
... removing the tick and tick++ from the original; code. Then you don't need a delay in the loop above because all the timing is pegged to the value of tick. However should you want a delay for other reasons then:
delayms( uint32_t millisec )
{
uint32_t start = tick ;
while( tick - start < millisec ) ;
}
Then you would initialise the SYSTICK at start-up thus:
int main (void)
{
SysTick_Config(SystemCoreClock / 1000) ;
...
}
This assumes that you are using the CMSIS, but your code suggests that you are not doing that (or even using a vendor supplied register header). You will in that case need to get down and dirty with the SYSTICK and NVIC registers if you (or your tutor) insists on that. The source for SysTick_Config() is as follows:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
Hi I am currently working on USART communication trying to transmit and receive data from any GPIO pin.
I am succeed to transmit data at any baud-rate when it comes to receiving i got stuck at a point.
I was able to receive a character at a time. Pin is set as external falling edge interrupt used a RX pin.
But when i transmit a string like "test" from terminal to controller only "t" is received rest 3 character is garbage value. I was thinking that after receiving first character and saving it, the Interrupt is not triggered as fast for next character.
Many things are hard coded in this sample code for test purpose.
Here the sample code for receiver
void EXTI0_IRQHandler(void){
r0 = GPIOA->IDR;
delay_us(delay_time);
r1 = GPIOA->IDR;
delay_us(delay_time);
r2 = GPIOA->IDR;
delay_us(delay_time);
r3 = GPIOA->IDR;
delay_us(delay_time);
r4 = GPIOA->IDR;
delay_us(delay_time);
r5 = GPIOA->IDR;
delay_us(delay_time);
r6 = GPIOA->IDR;
delay_us(delay_time);
r7 = GPIOA->IDR;
delay_us(delay_time);
r8 = GPIOA->IDR;
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
r1 = r1 & 0x00000001;
r2 = r2 & 0x00000001;
r3 = r3 & 0x00000001;
r4 = r4 & 0x00000001;
r5 = r5 & 0x00000001;
r6 = r6 & 0x00000001;
r7 = r7 & 0x00000001;
r8 = r8 & 0x00000001;
x |= r8;
x = x << 1;
x |= r7;
x = x << 1;
x |= r6;
x = x << 1;
x |= r5;
x = x << 1;
x |= r4;
x = x << 1;
x |= r3;
x = x << 1;
x |= r2;
x = x << 1;
x |= r1;
buff1[z++] = x;
EXTI->PR |= 0X00000001;
x=0;
return ;}
Thanks for your help.
The fundamental problem with your solution is that you are sampling the bits at the transition point rather then the bit centre. On detection of the START transition, you delay one bit period only, so sample r1 at the bit transition rather then the bit centre - this will almost certainly result in errors, especially at high speed where the edges may not be very fast. The first delay should be 1.5 bit periods long. (delay_time * 2 / 3) as illustrated below:
A second problem is that you unnecessarily delay after the STOP bit, which will cause you to miss the next START transition because it may occur before you clear the interrupt flag. Your work is done as soon as you have r8.
Sampling r0 and r9 serves no purpose you discard them in any case, and the state r0 is implicit in any event form the EXTI transition, and r9 would only not be 1 if the sender was generating invalid frames. Moreover if you are not sampling r9 the delay before it also becomes unnecessary. These lines should be removed:
delay_us(delay_time);
r9 = GPIOA->IDR;
delay_us(delay_time);
That would at least give you two bit periods where your processor could do other work other then being stuck in the interrupt context, but delaying is an interrupt handler is not good practice - it blocks execution of normal code and all lower priority interrupts making the solution unsuited to real-time systems. In this case if the soft-UART Rx is all the system has to do, you are likely to get better results by simply polling the GPIO rather then using interrupts - at least then other interrupts could run normally, and it is much simpler to implement.
Your "unrolled-loop" implementation also serves no real purpose with the delays in place - even at very high bit rates a loop overhead is likely to be insignificant over the duration of the frame, and if it were you could tweak the delays a little to compensate:
void EXTI0_IRQHandler(void)
{
delay_us(delay_time * 2 / 3);
for( int i = 7; i >= 0; i-- )
{
x |= GPIOA->IDR << i ;
delay_us(delay_time);
}
EXTI->PR |= 0X00000001;
buff1[z++] = x;
x = 0 ;
return ;
}
A more robust solution for a soft receiver that will play well with other processes in your system, should use the EXTI interrupt only to detect the start bit; the handler should disable the EXTI, and start a timer at the baud rate plus half a bit period. The interrupt handler for the timer, samples the GPIO pin at the centre of the bit period, and on the first interrupt after the EXTI, changes the period to one bit period. For each timer interrupt it samples and counts the bits until a whole data word has been shifted in, when it disables the timer and re-enables the EXTI for the next start bit.
I have successfully used this technique on STM32 running at 120MHz at 4800 and pushed it to 38400, but at 26 microseconds per bit it gets quite busy in the interrupt context, and your application presumably has other things to do?
The following is a slightly genericised version of my implementation. It uses STM32 Standard Peripheral Library calls rather then direct register access or the later STM32Cube HAL, but you can easily port it one way or the other as you need. The framing is N,8,1.
#define SOFT_RX__BAUD = 4800u ;
#define SOFT_RX_TIMER_RELOAD = 100u ;
void softRxInit( void )
{
// Enable SYSCFG clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
// Connect the EXTI Line to GPIO Pin
SYSCFG_EXTILineConfig( EXTI_PortSourceGPIOB, EXTI_PinSource0 );
TIM_Cmd( TIM10, DISABLE);
// NVIC initialisation
NVIC_InitTypeDef NVIC_InitStructure = {0,0,0,DISABLE};
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable peripheral clock to timers
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
TIM_ARRPreloadConfig( TIM10, DISABLE );
// Generate soft Rx rate clock (4800 Baud)
TIM_TimeBaseInitTypeDef init = {0};
TIM_TimeBaseStructInit( &init ) ;
init.TIM_Period = static_cast<uint32_t>( SOFT_RX_TIMER_RELOAD );
init.TIM_Prescaler = static_cast<uint16_t>( (TIM10_ClockRate() / (SOFT_RX__BAUD * SOFT_RX_TIMER_RELOAD)) - 1 );
init.TIM_ClockDivision = 0;
init.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM10, &init ) ;
// Enable the EXTI Interrupt in the NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 12;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
// Dummy call to handler to force initialisation
// of UART frame state machine
softRxHandler() ;
}
// Soft UART Rx START-bit interrupt handler
void EXTI0_IRQHandler()
{
// Shared interrupt, so verify that it is the correct one
if( EXTI_GetFlagStatus( EXTI_Line0 ) == SET )
{
// Clear the EXTI line pending bit.
// Same as EXTI_ClearITPendingBit( EXTI_Line11 )
EXTI_ClearFlag( EXTI_Line0 ) ;
// Call Soft UART Rx handler
softRxHandler() ;
}
}
void TIM1_UP_TIM10_IRQHandler( void )
{
// Call Soft UART Rx handler
softRxHandler() ;
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
}
// Handler for software UART Rx
inline void softRxHandler()
{
static const int START_BIT = -1 ;
static const int STOP_BIT = 8 ;
static const int HALF_BIT = SOFT_RX_TIMER_RELOAD / 2;
static const int FULL_BIT = SOFT_RX_TIMER_RELOAD ;
static int rx_bit_n = STOP_BIT ;
static const uint8_t RXDATA_MSB = 0x80 ;
static uint8_t rx_data = 0 ;
static EXTI_InitTypeDef extiInit = { EXTI_Line0,
EXTI_Mode_Interrupt,
EXTI_Trigger_Falling,
DISABLE } ;
// Switch START-bit/DATA-bit
switch( rx_bit_n )
{
case START_BIT :
{
// Stop waiting for START_BIT
extiInit.EXTI_LineCmd = DISABLE;
EXTI_Init( &extiInit );
// Enable the Interrupt
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
TIM_ITConfig( TIM10, TIM_IT_Update, ENABLE );
// Enable the timer (TIM10)
// Set time to hit centre of data LSB
TIM_SetAutoreload( TIM10, FULL_BIT + HALF_BIT ) ;
TIM_Cmd( TIM10, ENABLE );
// Next = LSB data
rx_data = 0 ;
rx_bit_n++ ;
}
break ;
// STOP_BIT is only set on first-time initialisation as a state, othewise it is
// transient within this scase.
// Use fall through and conditional test to allow
// case to handle both initialisation and UART-frame (N,8,1) restart.
case STOP_BIT :
default : // Data bits
{
TIM_ClearITPendingBit( TIM10, TIM_IT_Update );
if( rx_bit_n < STOP_BIT )
{
if( rx_bit_n == 0 )
{
// On LSB reset time to hit centre of successive bits
TIM_SetAutoreload( TIM10, FULL_BIT ) ;
}
// Shift last bit toward LSB (emulate UART shift register)
rx_data >>= 1 ;
// Read Rx bit from GPIO
if( GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_0 ) != 0 )
{
rx_data |= RXDATA_MSB ;
}
// Next bit
rx_bit_n++ ;
}
// If initial state or last DATA bit sampled...
if( rx_bit_n == STOP_BIT )
{
// Stop DATA-bit sample timer
TIM_Cmd( TIM10, DISABLE );
// Wait for new START-bit
rx_bit_n = START_BIT ;
extiInit.EXTI_LineCmd = ENABLE;
EXTI_Init( &extiInit );
// Place character in Rx buffer
serialReceive( rx_data ) ;
}
}
break ;
}
}
The code works in the same way as a real UART as illustrated in the timing diagrem above with the exception that in my implementation the STOP bit is not actually sampled - it is unnecessary; it only serves to ensure that the subsequent START bit is a 1 -> 0 transition and can generally be ignored. A real UART would probably generate a framing error if it were not 1, but if you were not going to handle such errors in any event, there is no purpose in checking.
I can't see in your code where you take account of the start bit that is normally part of a serial transmission. You seem to be only looking for 8 data bits and a stop bit.
With the convention of "start bit is the inverse of stop bit" there will be an additional edge your code detects between characters, thus apparently shifting the bit stream you detect by one bit.
You mentioned that character 't' is received when string "test" is sent.
Introduce sufficient inter character delay in the string.
Hopefully it works.
You can use docklite for sending string with inter character delay.
#include <avr/io.h>
#include <stdint.h>
// Ceramic Resonator
#ifndef F_CPU
#define F_CPU 3686400 // 4MHz
#endif
// UART
#define UART_BAUD_RATE 9600
#define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_CPU)/((UART_BAUD_RATE)*16L)-1)
int main(void)
{
// USART
UBRR0H =(uint8_t) (UART_BAUD_CALC(UART_BAUD_RATE,F_CPU) >>8);
UBRR0L =(uint8_t) UART_BAUD_CALC(UART_BAUD_RATE,F_CPU);
UCSR0B = (1<<RXEN0) | (1<<TXEN0); // enable receiver and transmitter,
UCSR0C = (3<<UCSZ00); // 8 bit (default: asynchronous, no parity, 1 stop-bit)
DDRC = (1<<5); // set data direction register bit 5 to one, this means PC5 is configured as output
PORTC = (1<<5); // set output value of PC5 to High-Level (Source Current, 5V to ground)
// VARIABLES
//uint8_t get;
// PROGRAM
unsigned char code[3] = {'x','y','z'}; // Here you need to write your code
unsigned char rcv[3]={'0','0','0'}; // received data
int i = 0;
while(1)
{
i = 0;
for(i=0;i<=2;i++)
{
// wait for empty transmit buffer
//while (!(UCSR0A & (1<<UDRE0)));
// wait for data to be received
while (!(UCSR0A & (1<<RXC0)));
/* put data into buffer, sends the data*/
{
code[i]= UDR0 ;
}
PORTC ^= (1<<5); //this is for LED
// get received data from buffer
rcv[i] = code[i];
}
// wait for empty transmit buffer
while (!(UCSR0A & (1<<UDRE0)));
// put data into buffer, sends the data
if ((rcv[0] == rcv[1]) && (rcv[0] == rcv[2]) && (rcv[1] == rcv[2]))
UDR0 = 00;
else UDR0 = 01;
}
}
This is my program where I send a data from PC to micro controller (Atmega 168PA by Docklight). I send three identical bytes. Then the micro controller must compare them and send me a boolean value, indicating whether they're equal (like 00) or not (like 01).
Next task is to verify where is the error happens or which byte is an error.
Also when the micro controller receives a data it stores three bytes as one data (data[i]) it is just repeated three times.
So if i send a data somehow('18' '19' '18') it must tell me where the error occured...
looks like you have setup the usart on usart0, set baudrate and enable tx and rx with no parity and 1 stop bit. Inside while loop you are taking data from usart read buffer using without interrupts mechanism and checking some condition and giving 01 or 00 as output. You should rather try UDR0 = 0x00 else UDR0 = 0x01;
Friends , I have to sample the input , every 14 microsecond in a 61 micro second slot with the timer input (Project Requirement).
I have to do it 8 times to make a byte. More like UART , but I am using it for One Wire Bus communication for my Masters Project.
I have written the code as below , which gives expected result, tested it executing one instruction at a time in debugger.
Below is the Code.
/*****************************************************************************
COMPARE MODE SAMPLING:
MCLK and SCLK #8MZ
The code configures P2.1 as TA1.CCI1A input.
It samples the input at P2.1 Whenever the TA1R reaches the TA1CCR1 value.
It samples input on P2.1 every 14us once in a duration of 61 us.
It then reads 8 bits one by one to read a byte.
******************************************************************************/
#include "io430g2553.h"
#define MSP_DQ BIT5
unsigned char word=0x00;
unsigned char i=0;
unsigned char temp;
void Read(void)
{
TA1CCR0 = 0x1E8; // 61 micro secs
TA1CCR1 = 0x70; // 14 micro secs
//TA0CCTL1 = CM_2 | CCIS_0 | SCS | CAP | OUTMOD_0 | CCIE;
//Keep in mind that It should not be configured as campture mode
TA1CCTL1 |= CM_2 | CCIS_0 | SCS | OUTMOD_0 | CCIE;
TA1CTL = TASSEL_2 + MC_1 + ID_0; // Register TA0CTL -> SMCLK/1, Up mode
do{
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
**TA1CCTL0 &= ~CCIFG; // Clear the flag** (%1%)
//TA1CTL &= ~TAIFG; // Clear the flag
i++;
} while( i<8) ;
TA1CTL = TACLR; // Stop the Timer
TA1CCTL1 = 0x00;
}
void Configure_CCI1A(void)
{
// Configuring P2.1 as TA1.CCI1A
P2OUT &= 0x00; // Clearing P1OUT
P2DIR &= ~BIT1 ; // Configuring P1.2 as input
P2SEL |= BIT1 ; // P2.1 Timer1_A, capture: CCI1A input, compare: Out1 output
P2SEL2 &= ~BIT1 ;
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
P1OUT &= 0x00; // Clearing P1OUT
P1DIR |= BIT0 ; // Configuring P1.0 as Output
__enable_interrupt();
Configure_CCI1A();
Read();
**P1OUT ^= BIT0;** //(%2%)
while(1) {
}
}
// Timer A1 interrupt service routine
#pragma vector=TIMER1_A1_VECTOR
__interrupt void Timer1_A1 (void)
{
P1OUT ^= BIT0; // To show Read occured
word <<=1; // If x = 00000010 (binary) => x <<= 2; => x=00001000
temp=0x00;
temp=((TA1CCTL1 & SCCI)>>10);
**word = word + temp ;** //(%3%)
}
But the issue is that when I call the function it some how appears stuck . I guess it is not coming out of the ISR cleanly though it completed all its execution when I run in debugger one instruction at a time. To make my question clear , This is how I tested :
Suppose if I put toggle break point at highlighted expression (%3%) in ISR , then it enters the ISR hits the togglebreak 8 times capturing correct values and comes out of Read Function neatly ( and also
while ((TA1CCTL0 & CCIFG) == 0 ) // wait while CCIF is set
{
}
and
{
....
....
i++;
} while( i<8) ;
out of above loop)
, to reach while(1) expression in the mains.
But instead if i put toggle point at the highlighted expression (%1%) it appears stuck.
OR if I put the toggle break point directly at (%2%) in main what i expect , it to complete the Read function , store the value in word variable to reach the toggle break point but the code appears stuck and does not hit the togglebreak.
I do not know what is wrong, can any one please help ?
The interrupt flag is automatically cleared when you read TAIV or if you manually clear it in TACCTL1. However, you do neither of these things in your ISR so the interrupt remains pending and your CPU continually executes your ISR and no other code so it never gets a chance to exit Read().
My guess is that by putting a breakpoint in the ISR your development environment causes a read from TAIV and clears the pending interrupt. I've experienced that before, but not sure how common that behaviour is as it is undesirable.