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.
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.
OK so I have been attempting to create some code using a MSP430FR5994 TI launch pad that utilizes Timer0 and 3 separate compare registers to trigger 3 separate isr's. I have successfully got one to work however as soon as I add another compare register the CCIFE flag sets and never competes the execution of the second isr. I have watched the code in the debugger on both CCstudio and IAR same thing happens in both, the set up registers are correct and the TA0R registers is counting and will trigger the first isr based on the TA0CCR0 but all other compare regs R1 2 3 etc will not trigger and execute successfully. The code is below, idea's on what I am doing wrong would be much appreciated.
#include "msp430.h"
#include <stdbool.h>
#define COUNT_1 12000
#define COUNT_2 800
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
PM5CTL0 &= ~LOCKLPM5;
P1DIR |= BIT0 + BIT1;
P1OUT = BIT0 + BIT1;
//set up and enable timer A or TA0 for continous mode
TA0CCR0 = COUNT_1;
TA1CCR1 = COUNT_2;
TA0CTL = TASSEL__ACLK + MC_2; //set the max period for 16bit timer operation
TA1CTL = TASSEL__ACLK + MC_2;
TA0CCTL0 = CCIE; //enable compare reg 0
TA1CCTL1 = CCIE; //enable compare reg 1
//TA0CTL |= TAIE;
_BIS_SR( GIE); //ENABLE GLOBAL INTERRRUPTS
//set the max period for 16bit timer operation
while(true){}
}
#pragma vector= TIMER0_A0_VECTOR //compare interrupt 0 flahse red led
__interrupt void TIMER0_A0(void) {
P1OUT ^= BIT1 ;
}
#pragma vector = TIMER1_A1_VECTOR //compare interrupt 1 flashes green led
__interrupt void TIMER1_A1(void) {
P1OUT ^= BIT0;
}
The User's Guide says in section 25.2.6.1:
The TAxCCR0 CCIFG flag is automatically reset when the TAxCCR0 interrupt
request is serviced.
However, this does not happen for the other CCRx interrupts, because multiple ones use the same interrupt vector.
Section 25.2.5.2 says:
The highest-priority enabled interrupt generates a number in the TAxIV register (see register description). […]
Any access, read or write, of the TAxIV register automatically resets the highest-pending interrupt flag.
So you always have to read the TAxIV register (and with three or more CCRs, you need it to find out which CCR triggered the interrupt):
__interrupt void TIMER1_A1(void) {
switch (TA1IV) {
case TAIV__TACCR1:
P1OUT ^= BIT0;
break;
case TAIV__TACCR2:
...
break;
}
}
The While(busy); loop is instantly skipped. But the only place where busy can be set to 0 is in the Timer1 ISR. But Timer 1 is stopped and only ever starts when in the Pin Change ISR.
From the UART output I can tell that Timer 1 ISR happens, while Pin Change ISR never does. which should not be possible, right?
What am I missing?
In my main function:
...
uint32_t temp = 0;
busy = 1;
mode = 1;
// Timer Interrupt Init
TCCR1B &= ~((1<<2) | (1<<1) | (1<<0)); // Makeing sure timer is not running
TIMSK1 |= (1 << TOIE1); // Timer 1 overflow interrupt enable
TCNT1 = 0; // Makeing sure Timer is on 0
// Pin Change Interrupt Init
PCICR |= (1<<2); // Activating PCMSK2
PCMSK2 |= (1<<6); // PCMSK2 -> PCINT23.. 16 seem to correspond to physical pins D 0-7
UartSendstring("1");
// Scanning (see ISR)
sei();
TCCR1B &= ~((1<<2) | (1<<1) | (1<<0));
while(busy);
cli();
...
Timer 1 ISR:
ISR(TIMER1_OVF_vect)
{
UartSendstring("3");
busy = 0;
}
Pin Change ISR:
ISR(PCINT2_vect)
{
UartSendstring("2");
//todo make first values not empty
TCCR1B &= ~((1<<2) | (1<<1) | (1<<0));// CS12 - CS10 are set to 0 to stop the timer
data[addr] |= TCNT1L;
data[addr] |= (TCNT1H << 8); // High and low byte are saved to data
TCNT1 = 0; // Timer is reset
TCCR1B |= ((1<<1) | (1<<0)); // CS12 is set to 1 to restart the timer with prescaler 64 -> tick time = 4us
// Signal period duration is 1s / 38 000 = 26us
// -> at least on timer tick in one signal period
addr++; // Prepares to write to the next address with next edge
}
Uart output is:
13
edit
I tried moving the TIMSK1 |= (1 << TOIE1); to the Pin Change ISR. Now it goes in there at least once like I want it but as soon as I enable the Interrupt it triggers the ISR again and ends.
As the Arduino core starts all timers by default (because of PWM), there is possibility that interrupt flags are already set and they fires as soon as you enable the corresponding interrupts. So you have to clear them before reenabling interrupts. But there is a tiny little obstacle: interrupt flags are cleared by writing logic one into corresponding bit. Therefore you have to use something like this TIFR1 = _BV(ICF1) | _BV(OCF1B) | _BV(OCF1A) | _BV(TOV1); (however as you don't use any other Timer1 interrupt, you can clear TOV1 flag only).
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.
I have an atmega168a chip. I use Counter 0 to toggle PORTC by using ISR(TIMER0_COMPA_vect) and ISR(TIMERB_COMPA_vect) interrupt sub-routines. I would like to activate the 16-bit timer when if condition is true. So, I use TIMSK1 = (1<<OCIE1A), but this line calls ISR(TIMER1_COMPA_vect) interrupt instantly where I want 16 bit timer to be interrupted only when the counter reaches to OCR1A value. How can I activate the 16-bit timer on the run-time without causing an instant interrupt?
here is my code:
#define F_CPU 8000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
volatile uint8_t counter;
int main (void){
DDRC = 0xFF; //sets PORTC as output
PORTC = 0xFF; //initial output value
/*COUNTER0 settings*/
TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B)); // Enable Interrupt TimerCounter0 Compare Match A & B
TCCR0A = (1<<WGM01); // Mode = CTC
TCCR0B = (1<<CS01) | (1<<CS00); // Clock/64, 1/(8000000/64)= 0.000008 seconds per tick
OCR0A = 200; // 0.000008 *230 = 1.6 ms
OCR0B = 100; // 0.8 ms
/*16bit timer - counter1 settings*/
TIMSK1 &= ~(1<<OCIE1A); // Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12)); // Clock/1024, 1/(8000000/1024) = 0.000128 seconds per tick, Mode=CTC
OCR1A = 40; // 0.000128*40 ~= 5.12 milliseconds
sei(); //interrupts are globally switched on
counter =0;
while(1){
if(counter >= 4){
TCNT1 = 0; // clear the counter 1
TIMSK1 = (1<<OCIE1A);// Enables the interrupt for Counter 1,(TIMER1_CMPA_vect)
TIMSK0 &= ~((1<<OCIE0A) | (1<<OCIE0B)); //disables the Counter 0's interrupts
counter = 0;
}
}
return 0;
}
ISR(TIMER0_COMPA_vect){ //1.6ms
PORTC = 0xFF;
counter++;
}
ISR(TIMER0_COMPB_vect){ //0.8 ms
PORTC = ~PORTC;
}
ISR(TIMER1_COMPA_vect){ // 5.2 milisecond interrupt
PORTC = 0x00;
TCNT0 = 0; //clear the counter of counter0
// TIMSK0 = ((1<<OCIE0A) | (1<<OCIE0B)); //Enable the Counter 0 interrupts
// TIMSK1 &= ~(1<<OCIE1A);// Disable Interrupt Counter 1, output compare A (TIMER1_CMPA_vect)
}
here is an oscilloscope output that shows why I don't want the interrupt to be set instantly, because it set the signal 0 instantly.
I think the problem might be that in CTC mode, interrupt is generated when OCF1A flag is set (in TIFR). Since your timer is always running, just not generating interrupts, it sets OCF1A flag, which never gets cleared. On page 142 in the datasheet it says:
OCF1B is automatically cleared when the Output Compare Match B
Interrupt Vector is executed. Alternatively, OCF1B can be cleared by
writing a logic one to its bit location.
This means that when you set up timer 1, you also need to clear OCF1A:
TIFR1 &= ~(1<<OCF1A)
However, I think you can do better. You could just stop the timer when not needed, and start it when you do, instead of twiddling the TIMSK and having timer 1 run always. If you set TCCR1B to zero, that clears CS12, CS11, and CS10, which, according to the datasheet means "Timer stopped." Then, when your counter reaches 4 you can turn on timer1 as you have it above:
TCCR1B = ((1<<CS12) | (1<<CS10) | (1<<WGM12));
If you do this, you shouldn't need to turn timer 1 interrupts on and off: just leave them on, and only turn the counting on when you need it.
Also I am wondering if it is actually necessary to fire off two interrupts to toggle pins on PORTC? Are you not using PWM for that because it doesn't give you the pulse lengths precisely enough?