My name is Adam and I will use this as a hello if that is ok with folks. I am a long time lurker. I have banged my head long enough with this and now I will suck it up and as for help.
I have an attiny84 with a 4 segment spatially interpolated slider. The 8-bit Slider position = 8-bit PWM duty cycle for LED driver control (TPS61165). I am getting a flicker in my LED's that corresponds to the Qmatrix 4 channel Burst. I am not sure if this is because of a timer conflict or poor code.
I do know that my input power is out of Vripple spec for Qmatrix(approx 20mvpp) I am about 40mvpp. I have a new hardware revision that should filter that out more (CLCLC filter before LDO). But it seems like its more of a software issue.
From the following Atmel Webdoc:
http://www.atmel.com/webdoc/QTSoftwareLibUserGuide/QT_SQ_LIB.section_puz_yto_ag.html#QT_SQ_LIB.section_xwz_yto_agWe can see that Qmatrix uses Timer1 internally all the time or just sometimes?
General application notes
1 The clock, host application and other peripherals needed by the host application needs to be initialized.
2 The QMatrix acquisition method libraries internally use TIMER1 for their operation.
3 Ensure that there are no conflicts between the resources used by the touch library and the host application
4 Ensure that the stack size is adjusted to factor in the stack depth required for the operation of the touch libraries.
and
Resources used by QMatrix acquisition method libraries
The following additional resources are used by the QMatrix acquisition method libraries.
1 One Analog Comparator
2 One internal Timer ( Usually Timer1 depending on the availability on particular microcontroller)
3 One ADC Multiplexer( The critical section of the touch sensing library disables the use of ADC as conversion unit and enables the same ADC as a multiplexer, but the user can use the ADC for conversion in rest of his application code )
The ADCMUX is used by the library during the touch sensing acquisition, however it is restored with the value from host application before exiting the qt_measure_sensors() such that the ADC is available to the host application for conversion.
Hmm what is it? Because I am using Timer1 for my PWM output BUT my Studio code shows that Qmatrix is using Timer0 as far as I can tell. Unless I cannot see where Timer1 would be implemented by Qmatrix.
Here is the Code:
init_mcu_attiny84.c showing Timer0 being called.
#include <avr/io.h>
#include <avr/interrupt.h>
#include "touch.h"
/*----------------------------------------------------------------------------
manifest constants
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
type definitions
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
prototypes
----------------------------------------------------------------------------*/
/* configure timer ISR to fire regularly */
void init_timer_isr( void );
/* initialise host app, pins, watchdog, etc */
void init_system( void );
/*----------------------------------------------------------------------------
Structure Declarations
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
macros
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
global variables
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
extern variables
----------------------------------------------------------------------------*/
/* Timer period in msec. */
extern uint16_t qt_measurement_period_msec;
extern uint16_t time_ms_inc;
/*----------------------------------------------------------------------------
static variables
----------------------------------------------------------------------------*/
/* flag set by timer ISR when it's time to measure touch */
extern volatile uint8_t time_to_measure_touch;
/* current time, set by timer ISR */
extern volatile uint16_t current_time_ms_touch;
#if defined(__ATtiny84__)
#if defined(_QTOUCH_) || defined(_QMATRIX_)
/*============================================================================
Name : init_timer_isr
------------------------------------------------------------------------------
Purpose : configure timer ISR to fire regularly
Input : n/a
Output : n/a
Notes :
============================================================================*/
void init_timer_isr( void )
{
/* set timer compare value (how often timer ISR will fire set to 1 ms timer interrupt) */
OCR0A = ( TICKS_PER_MS * QT_TIMER_PERIOD_MSEC);
/* enable timer ISR on compare A */
TIMSK0 |= ( 1 << OCIE0A );
/* timer prescaler = system clock / 64 */
TCCR0B |= (1 << CS01) | (1 << CS00);
/* timer mode = CTC (count up to compare value, then reset) */
TCCR0A |= (1 << WGM01);
}
/*============================================================================
Name : timer_isr
------------------------------------------------------------------------------
Purpose : timer 0 compare ISR
Input : n/a
Output : n/a
Notes :
============================================================================*/
ISR(TIM0_COMPA_vect)
{
#ifdef QDEBUG_TWI
if (gMsTimeout)
{
gMsTimeout--;
}
#endif
time_ms_inc += QT_TIMER_PERIOD_MSEC;
if(time_ms_inc >= qt_measurement_period_msec)
{
time_ms_inc =0;
/* set flag: it's time to measure touch */
time_to_measure_touch = 1u;
}
else
{
}
/* update the current time */
current_time_ms_touch += QT_TIMER_PERIOD_MSEC;
}
#endif /* TECHNOLOGY */
/*============================================================================
Name : init_system
------------------------------------------------------------------------------
Purpose : initialise host app, pins, watchdog, etc
============================================================================*/
void init_system( void )
{
/* run at 4MHz (assuming internal clock is set to 8MHz)*/
CLKPR = 0x80u;
CLKPR = 0x01u;
/* disable pull-ups */
MCUCR |= (1u << PUD);
}
#endif
main.c showing my PWM code
#include <avr/io.h>
#include <avr/interrupt.h>
#define __delay_cycles(n) __builtin_avr_delay_cycles(n)
#define __enable_interrupt() sei()
#include "touch_api.h"
#include "touch.h"
/*----------------------------------------------------------------------------
manifest constants
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
macros
----------------------------------------------------------------------------*/
#define GET_SENSOR_STATE(SENSOR_NUMBER) qt_measure_data.qt_touch_status.sensor_states[(SENSOR_NUMBER/8)] & (1 << (SENSOR_NUMBER % 8))
#define GET_ROTOR_SLIDER_POSITION(ROTOR_SLIDER_NUMBER) qt_measure_data.qt_touch_status.rotor_slider_values[ROTOR_SLIDER_NUMBER]
/*----------------------------------------------------------------------------
type definitions
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
prototypes
----------------------------------------------------------------------------*/
extern void touch_measure(void);
extern void touch_init( void );
extern void init_system( void );
extern void init_timer_isr(void);
extern void set_timer_period(uint16_t);
/*----------------------------------------------------------------------------
Structure Declarations
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
macros
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
global variables
----------------------------------------------------------------------------*/
/* Timer period in msec. */
uint16_t qt_measurement_period_msec = QT_MEASUREMENT_PERIOD_MS;
uint16_t time_ms_inc=0;
/*----------------------------------------------------------------------------
extern variables
----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
static variables
----------------------------------------------------------------------------*/
/* flag set by timer ISR when it's time to measure touch */
volatile uint8_t time_to_measure_touch = 0u;
/* current time, set by timer ISR */
volatile uint16_t current_time_ms_touch = 0u;
/*============================================================================
Name : main
------------------------------------------------------------------------------
Purpose : main code entry point
Input : n/a
Output : n/a
Notes :
============================================================================*/
int main( void )
{
uint8_t slider_value;
/* initialise host app, pins, watchdog, etc */
init_system();
/* configure timer ISR to fire regularly */
init_timer_isr();
/* Initialize Touch sensors */
touch_init();
/* enable interrupts */
__enable_interrupt();
/* loop forever */
for( ; ; )
{
touch_measure();
/* Time Non-critical host application code goes here */
if (GET_SENSOR_STATE(0))
{
slider_value = GET_ROTOR_SLIDER_POSITION(0);
// Doubles clock frequency, doing this because Phase Correct PWM halves frequency
// due to Bottom-Top-Bottom counting - OSCCAL (Trim internal clock)
//OSCCAL = 0xFF;
// Waveform Generator Mode 11 set with WGM11 set to 1 and WGM10 set to 1
// (1<<COM1B1) Clear OC1A/OC1B on Compare Match when up-counting.
// Set OC1A/OC1B on Compare Match when down-counting.
TCCR1A |= (1<<WGM11)|(1<<WGM10)|(1<<COM1B1);
// Waveform Generator Mode 11 set with WGM13 set to 1 and WGM12 set to 0
// (1<<CS10) = no prescaler
TCCR1B |= (1<<WGM13)|(1<<CS10);
// OCR1A sets TOP for Waveform Generator Mode 11
OCR1A = 255;
// Sets PortA Pin5 as output - IC actual pin 20 OC1B
DDRA |= (1<<DDA5);
// Duty cycle for waveform = (OCR1B/255) this will be the slider position variable
OCR1B = slider_value;
}
}
}
Captured waveforms are attached showing the issue. CH1 is the 4 channels of burst and CH2 is my PWM output to the LED Driver.
Any ideas?
Should I abandon the attiny84 and use the atmega328PB with its 3 timers and PTC? I do have 16 boards that use this attiny84 revision so that would suck a bit.
Does my code stink?
I appreciate all the help.
Adam