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:
We can see that Qmatrix uses Timer1 internally all the time or just sometimes?
-
General application notes
-
The clock, host application and other peripherals needed by the host application needs to be initialized.
-
The QMatrix acquisition method libraries internally use TIMER1 for their operation.
-
Ensure that there are no conflicts between the resources used by the touch library and the host application
-
Ensure that the stack size is adjusted to factor in the stack depth required for the operation of the touch libraries.
-
Resources used by QMatrix acquisition method libraries
The following additional resources are used by the QMatrix acquisition method libraries.
-
One Analog Comparator
-
One internal Timer ( Usually Timer1 depending on the availability on particular microcontroller)
-
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;
}
}
}
touch.c
#include <avr/io.h> #include <avr/interrupt.h> #define __delay_cycles(n) __builtin_avr_delay_cycles(n) /* now include touch api.h with the localization defined above */ #include "touch_api.h" #include "touch.h" #ifdef _ATXMEGA_ #include "asf.h" #endif #ifdef _DEBUG_INTERFACE_ /* Include files for QTouch Studio integration */ #include "QDebug.h" #include "QDebugTransport.h" #endif /*---------------------------------------------------------------------------- manifest constants ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- type definitions ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- prototypes ----------------------------------------------------------------------------*/ /* This will initialize touch related code */ void touch_init( void ); /* This will call all the functions for touch related measurement */ void touch_measure(void); /* Assign the parameters values to global configuration parameter structure */ static void qt_set_parameters( void ); /* Configure the sensors */ static void config_sensors(void); #ifdef _DEBUG_INTERFACE_ void set_timestamp1(void); // used for timestamping #endif /*---------------------------------------------------------------------------- Structure Declarations ----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------- macros ----------------------------------------------------------------------------*/ #ifdef _DEBUG_INTERFACE_ #ifdef _QDEBUG_TIME_STAMPS_ /* This below code is used for timestamping related information */ #define TIMESTAMP0 asm("cli"); timestamp0_lword = (uint16_t)TIMER_COUNTER_L;timestamp0_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp0_hword = current_time_ms_touch; asm("sei"); #define TIMESTAMP1 asm("cli"); timestamp1_lword = (uint16_t)TIMER_COUNTER_L;timestamp1_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp1_hword = current_time_ms_touch; asm("sei"); #define TIMESTAMP2 asm("cli"); timestamp2_lword = (uint16_t)TIMER_COUNTER_L;timestamp2_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp2_hword = current_time_ms_touch; asm("sei"); #define TIMESTAMP3 asm("cli"); timestamp3_lword = (uint16_t)TIMER_COUNTER_L;timestamp3_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp3_hword = current_time_ms_touch; asm("sei"); #define TIMESTAMP4 asm("cli"); timestamp4_lword = (uint16_t)TIMER_COUNTER_L;timestamp4_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp4_hword = current_time_ms_touch; asm("sei"); #define TIMESTAMP5 asm("cli"); timestamp5_lword = (uint16_t)TIMER_COUNTER_L;timestamp5_lword |= (uint16_t)(TIMER_COUNTER_H << 8); timestamp5_hword = current_time_ms_touch; asm("sei"); #else #define TIMESTAMP0 {} #define TIMESTAMP1 {} #define TIMESTAMP2 {} #define TIMESTAMP3 {} #define TIMESTAMP4 {} #define TIMESTAMP5 {} #endif #endif /*---------------------------------------------------------------------------- global variables ----------------------------------------------------------------------------*/ #ifdef _DEBUG_INTERFACE_ #ifdef _QDEBUG_TIME_STAMPS_ uint16_t timestamp0_hword=0; uint16_t timestamp0_lword=0; uint16_t timestamp1_hword=0; uint16_t timestamp1_lword=0; uint16_t timestamp2_hword=0; uint16_t timestamp2_lword=0; uint16_t timestamp3_hword=0; uint16_t timestamp3_lword=0; uint16_t timestamp4_hword=0; uint16_t timestamp4_lword=0; uint16_t timestamp5_hword=0; uint16_t timestamp5_lword=0; #endif #endif /*---------------------------------------------------------------------------- extern variables ----------------------------------------------------------------------------*/ /* This configuration data structure parameters if needs to be changed will be changed in the qt_set_parameters function */ extern qt_touch_lib_config_data_t qt_config_data; /* measurement data */ extern qt_touch_lib_measure_data_t qt_measure_data; qt_touch_lib_measure_data_t *pqt_measure_data = &qt_measure_data; /* Get sensor delta values */ extern int16_t qt_get_sensor_delta( uint8_t sensor); #ifdef _QMATRIX_ extern y_line_info_t y_line_info[NUM_Y_LINES]; extern x_line_info_t x_line_info[NUM_X_LINES]; /* Fill out the X-Line masks and Y-Line masks on the X- Port and Y-Line Port selected. * The order of X - Line numbering follows from the way the * */ /** * Number of Channels(dependent on the library used and application requirement). * The least possible number more that the application needs. * Please refer to the QTouch library user guide.pdf and library selection * guide.xls more information on selecting the number of channels. * * Possible values: 4, 8, 12, 16, 32. in case of QTouch * Possible values: 4, 8, 16, 32, 56, 64. in case of QMatrix. */ #define QT_NUM_CHANNELS 4 /** * Define the Number X lines to be used. * Possible values: 4 and 8 * Depending on the library used.Please refer to the QTouch library user guide.pdf * and library selection guide.xls more information on selecting the number of channels. * */#define NUM_X_LINES 4 /** * Specify the number of ports on which X-lines that are distributed. * Note: Support is provided only for a maximum of 3 ports for X. * ( Maximum possible value for NUM_X_PORTS is 3) * Possible values: 1,2,3 * Also, note that code memory increases with the number of ports * used for X lines. */#define NUM_X_PORTS 2 /** * Specify Which ports have X lines on them. These macros are used * to conditionally compile in support for ports driving X lines. */#define PORT_X_1 A #define PORT_X_2 B /** * Define the Number Y lines to be used. * Possible values: 1, 2, 4 and 8 * Depending on the library used.Please refer to the QTouch library user guide.pdf * and library selection guide.xls more information on selecting the number of channels. */ #define NUM_Y_LINES 1 /** * Specify the port for YA, YB, * For rules to specify the port for YA ,YB please go through QTouch library * user guide .pdf */ #define PORT_YA A #define PORT_YB A /** * SHARED_YA_YB should be 1 if YA and YB lines are on same port else 0 */ #define SHARED_YAYB 1 /** * Specify the port for SMP. * And Specify the pin for SMP on selected SMP port. * Any GPIO pin not conflicting with the other touch pins used for the application */ #define PORT_SMP A #define SMP_PIN 0 #define PORT_NUM_1 1 #define PORT_NUM_2 2 x_line_info_t x_line_info[NUM_X_LINES]= { FILL_OUT_X_LINE_INFO(1,7), FILL_OUT_X_LINE_INFO(2,2), FILL_OUT_X_LINE_INFO(2,1), FILL_OUT_X_LINE_INFO(2,0)}; y_line_info_t ya_line_info[NUM_Y_LINES]={ FILL_OUT_YA_LINE_INFO(3), }; y_line_info_t yb_line_info[NUM_Y_LINES]={ FILL_OUT_YB_LINE_INFO(2), }; #endif/*_QMATRIX_*/ extern uint8_t time_to_measure_touch; extern uint16_t current_time_ms_touch; /*---------------------------------------------------------------------------- static variables ----------------------------------------------------------------------------*/ /*============================================================================ Name : touch_init ------------------------------------------------------------------------------ Purpose : This will initialize touch related code. Input : n/a Output : n/a Notes : ============================================================================*/ void touch_init( void ) { #ifdef _ATXMEGA_ /* Enable clocks for the modules used by * QMatrix library */ /* Enable Event System module in Power reduction register */ sysclk_enable_module(SYSCLK_PORT_A, SYSCLK_AC); /* Enable Analog comparator module in Power reduction register */ sysclk_enable_module(SYSCLK_PORT_GEN, SYSCLK_EVSYS); /* Enable Timer/Counter1 in Power reduction register */ sysclk_enable_module(SYSCLK_PORT_C, SYSCLK_TC1); #endif /* Configure the Sensors as keys or Keys With Rotor/Sliders in this function */ config_sensors(); /* initialise touch sensing */ qt_init_sensing(); /* Set the parameters like recalibration threshold, Max_On_Duration etc in this function by the user */ qt_set_parameters( ); /* Address to pass address of user functions */ /* This function is called after the library has made capacitive measurements, * but before it has processed them. The user can use this hook to apply filter * functions to the measured signal values.(Possibly to fix sensor layout faults) */ #ifdef _DEBUG_INTERFACE_ qt_filter_callback = &set_timestamp1; #else qt_filter_callback = 0; #endif #ifdef _DEBUG_INTERFACE_ /* Initialize debug protocol */ QDebug_Init(); /* Process commands from PC */ QDebug_ProcessCommands(); #endif } #ifdef _DEBUG_INTERFACE_ void set_timestamp1(void) { TIMESTAMP1; } #endif /*============================================================================ Name : touch_measure ------------------------------------------------------------------------------ Purpose : This will call all the functions for touch related measurement. Input : n/a Output : n/a Notes : ============================================================================*/ void touch_measure(void) { /*status flags to indicate the re-burst for library*/ static uint16_t status_flag = 0u; static uint16_t burst_flag = 0u; if( time_to_measure_touch ) { /* clear flag: it's time to measure touch */ time_to_measure_touch = 0u; do { #ifdef _DEBUG_INTERFACE_ TIMESTAMP0; #endif /* one time measure touch sensors */ status_flag = qt_measure_sensors( current_time_ms_touch ); #ifdef _DEBUG_INTERFACE_ TIMESTAMP2; #endif burst_flag = status_flag & QTLIB_BURST_AGAIN; #ifdef _DEBUG_INTERFACE_ /* send debug data */ QDebug_SendData(status_flag); /* Process commands from PC */ QDebug_ProcessCommands(); #endif #ifdef _DEBUG_INTERFACE_ TIMESTAMP3; #endif /* Time-critical host application code goes here */ }while (burst_flag) ; } #ifdef _DEBUG_INTERFACE_ TIMESTAMP4; #endif /* Non-Time critical host application code goes here */ #ifdef _DEBUG_INTERFACE_ TIMESTAMP5; #endif /* Host sleep code goes here */ } /*============================================================================ Name : qt_set_parameters ------------------------------------------------------------------------------ Purpose : This will fill the default threshold values in the configuration data structure.But User can change the values of these parameters . Input : n/a Output : n/a Notes : Generated Code from QTouch Studio. Do not change ============================================================================*/ static void qt_set_parameters( void ) { /* This will be modified by the user to different values */ qt_burst_lengths[0]= 64; qt_burst_lengths[1]= 64; qt_burst_lengths[2]= 64; qt_burst_lengths[3]= 64; qt_config_data.qt_di = 20; qt_config_data.qt_neg_drift_rate = 10; qt_config_data.qt_pos_drift_rate = 10; qt_config_data.qt_max_on_duration = 20; qt_config_data.qt_drift_hold_time = 1; qt_config_data.qt_recal_threshold = 0; qt_config_data.qt_pos_recal_delay = 10; } /*============================================================================ Name : config_sensors - ------------------------------------------------------------------------------ Purpose : Configure the sensors Input : n/a Output : n/a Notes : Generated code from QTouch Studio. Do not change ============================================================================*/ static void config_sensors(void) { qt_enable_slider( CHANNEL_0, CHANNEL_3, NO_AKS_GROUP, 20u, HYST_25, RES_8_BIT, 0u ); }
touch.h
#define _QMATRIX_ /** * Device Name. * */ #define __ATtiny84__ /** * Delay cycles that determine the capacitance charge pulse width. Value of 0 * will enable charge pulse width of 1 clock cycle, 1 will enable a width of 2 * clock cycles and so on... * * Possible values: 1,2,3,4,5,10,25,50 */ #ifndef QT_DELAY_CYCLES #define QT_DELAY_CYCLES 1 #endif /** * Define the Number of ROTORS/SLIDERS used. * Possible values: 0 ( if _ROTOR_SLIDER_ is not defined) * 1, 2, 4 and 8 ( if _ROTOR_SLIDER_ is defined) * Depending on the library used.. */ #define QT_MAX_NUM_ROTORS_SLIDERS 1 #define _ROTOR_SLIDER_ /** * Number of Channels(dependent on the library used and application requirement). * The least possible number more that the application needs. * Please refer to the QTouch library user guide.pdf and library selection * guide.xls more information on selecting the number of channels. * * Possible values: 4, 8, 12, 16, 32. in case of QTouch * Possible values: 4, 8, 16, 32, 56, 64. in case of QMatrix. */ #define QT_NUM_CHANNELS 4 /** * Define the Number X lines to be used. * Possible values: 4 and 8 * Depending on the library used.Please refer to the QTouch library user guide.pdf * and library selection guide.xls more information on selecting the number of channels. * */#define NUM_X_LINES 4 /** * Specify the number of ports on which X-lines that are distributed. * Note: Support is provided only for a maximum of 3 ports for X. * ( Maximum possible value for NUM_X_PORTS is 3) * Possible values: 1,2,3 * Also, note that code memory increases with the number of ports * used for X lines. */#define NUM_X_PORTS 2 /** * Specify Which ports have X lines on them. These macros are used * to conditionally compile in support for ports driving X lines. */#define PORT_X_1 A #define PORT_X_2 B /** * Define the Number Y lines to be used. * Possible values: 1, 2, 4 and 8 * Depending on the library used.Please refer to the QTouch library user guide.pdf * and library selection guide.xls more information on selecting the number of channels. */ #define NUM_Y_LINES 1 /** * Specify the port for YA, YB, * For rules to specify the port for YA ,YB please go through QTouch library * user guide .pdf */ #define PORT_YA A #define PORT_YB A /** * SHARED_YA_YB should be 1 if YA and YB lines are on same port else 0 */ #define SHARED_YAYB 1 /** * Specify the port for SMP. * And Specify the pin for SMP on selected SMP port. * Any GPIO pin not conflicting with the other touch pins used for the application */ #define PORT_SMP A #define SMP_PIN 0 #define PORT_NUM_1 1 #define PORT_NUM_2 2 /** * Provide the number of timer clock ticks (cycles) required to provide a 1 millisecond time interval. * TICKS_PER_MS = (CLK_FREQ/TIMER_PRESCALER)*(1/1000) * Example, TICKS_PER_MS = (8MHz/64)*(1/1000) = 125 */ #define TICKS_PER_MS 63u /** * Provide the periodic interrupt interval for which the timer is configured. * Example, QT_TIMER_PERIOD_MSEC 2u * Timer ISR will fire at every 2 milliseconds. */ #define QT_TIMER_PERIOD_MSEC 1u /** * Provide the periodic interval for touch measurement. * Example, QT_MEASUREMENT_PERIOD_MS 50u * Perform a single touch measurement every 50msec. */ #define QT_MEASUREMENT_PERIOD_MS 25u
Here are the captured waveforms 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.