I'm trying to use the SAML21G18B MCU and the Mattairtech Arduino distribution, I need to configure TC0 to generate interrupts. Unfortunatly none of the existing SAMD timer libraries work with this MCU/distribution so I've been trying to configure and use TC0 from scratch ...
Mattairtech core on Github
I've taken the analogWrite() code from the Mattairtech core and hacked it into what I hope is something that configures TC0 for normal PWM - the same as when its used for analogWrite().
It compiles (YAY!) but I attempted to read from the count register to see if it is counting, and I get nothing. I also have no idea how to configure it to generate an interrupt on a match with the CC0 register.
All my code is below, any help, pointers, tips to get the interrupts working would be appreciated.
void TC0_Handler() {
//interrupt handler ...
}
static void SYNC_TC(Tc* TCx){
while(TCx->COUNT16.SYNCBUSY.reg & (TC_SYNCBUSY_SWRST | TC_SYNCBUSY_ENABLE | TC_SYNCBUSY_CTRLB | TC_SYNCBUSY_STATUS | TC_SYNCBUSY_COUNT));
}
//initialise TCO
void initTc0(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
//GCLK setup
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg & GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
//Disable TC
TCx->COUNT16.CTRLA.bit.ENABLE = 0;
SYNC_TC(TCx);
// Set Timer counter Mode to 16 bits, normal PWM
if(sixteenBit) TCx->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
else{
// Set Timer counter Mode to 8 bits, normal PWM
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8;
SYNC_TC(TCx);
// Set PER to maximum counter value
TCx->COUNT8.PER.reg = 0xFF;
}
SYNC_TC(TCx);
// Set TCx as normal PWM
TCx->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_NPWM;
SYNC_TC(TCx);
// Set the initial value for CC
if(sixteenBit) TCx->COUNT16.CC[timerCh].reg = (uint16_t) value;
else TCx->COUNT8.CC[timerCh].reg = (uint8_t) value;
SYNC_TC(TCx);
// Enable TCx
TCx->COUNT16.CTRLA.bit.ENABLE = 1;
SYNC_TC(TCx);
}
//Set the CCBuf register
void setTc(bool sixteenBit, uint16_t value){
uint8_t timerCh = 0;
Tc* TCx = (Tc*)TC0;
if(sixteenBit) TCx->COUNT16.CCBUF[timerCh].reg = (uint16_t)value;
else TCx->COUNT8.CCBUF[timerCh].reg = (uint8_t)value;
SYNC_TC(TCx);
}
uint16_t getCount(){
Tc* TCx = (Tc*)TC0;
TCx->COUNT16.CTRLBSET.bit.CMD = TC_CTRLBSET_CMD_READSYNC;
return TCx->COUNT16.COUNT.reg;
}
void setup() {
// put your setup code here, to run once:
initTc0(1, 0);
setTc(1, 500);
Serial.begin(1000000);
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(getCount());
delay(100);
}
Ok, I answered my own question by hacking the tone() function. It uses TC1 to generate periodic interrupts that toggle a pin on and off to generate the tone. The following code lets you configure the timer with a clock prescaler and a value for CC - the timer counts up and when it matches CC the interrupt is called and it starts over.
This works for TC0, and is easily edited for TC1
#include "variant.h"
#include "sam.h"
//***************Use this for TC0
#define TCTimer TC0
#define timerIRQ TC0_IRQn
//Timer handler function if using TC0 - put whatever ISR code you want in here.
void TC0_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts++;
}
//***************Use this for TC1
/*
#define TCTimer TC1
#define timerIRQ TC1_IRQn
//Timer handler function if using TC1 - put whatever ISR code you want in here.
void TC1_Handler (void){
//clear the interrupt flag
TCTimer->COUNT16.INTFLAG.bit.MC0 = 1;
isrCounts++;
}*/
//Wait for registers to synchronise
#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.reg);
#define TONE_TC_TOP 0xFFFF
#define TONE_TC_CHANNEL 0
//Reset the timer hardware
static inline void resetTC (Tc* TCx){
// Disable TCx
TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCx)
// Reset TCx
TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
WAIT_TC16_REGS_SYNC(TCx)
while (TCx->COUNT16.CTRLA.bit.SWRST);
}
//setup the timer with a prescaler clock value, and the CC value.
//The timer incriments until it hits CC, then fires an interrupt and starts over
//With prescaler at 1 and CC at 65535 it interrupts 733 times per second
//Reduce CC to get a faster interrupt rate
//Prescaler must only be 1,2,4,8,16,64,25 or 1024
void setupTCTimer(uint32_t prescaler, uint32_t ccVal ){
NVIC_DisableIRQ(timerIRQ);
NVIC_ClearPendingIRQ(timerIRQ);
NVIC_SetPriority(timerIRQ, 0);
// Enable GCLK for timer used
GCLK->PCHCTRL[GCM_TC0_TC1].reg = (GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0);
while ( (GCLK->PCHCTRL[GCM_TC0_TC1].reg & GCLK_PCHCTRL_CHEN) == 0 ); // wait for sync
uint32_t prescalerConfigBits;
uint32_t ccValue;
ccValue = ccVal;//toneMaxFrequency / frequency - 1;
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
switch(prescaler){
case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break;
case 4: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break;
case 8: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break;
case 16: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break;
case 64: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break;
case 256: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break;
case 1024: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break;
default: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; break;
}
resetTC(TCTimer);
uint16_t tmpReg = 0;
tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
tmpReg |= prescalerConfigBits;
TCTimer->COUNT16.CTRLA.reg |= tmpReg;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ;
WAIT_TC16_REGS_SYNC(TCTimer)
TCTimer->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
WAIT_TC16_REGS_SYNC(TCTimer)
// Enable the TCTimer interrupt request
TCTimer->COUNT16.INTENSET.bit.MC0 = 1;
}
//Start the timer
//you need to call setupTCTimer with prescaler and CC values before you start the timer
void startTCTimer(){
// Enable TCTimer
TCTimer->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
WAIT_TC16_REGS_SYNC(TCTimer)
NVIC_EnableIRQ(timerIRQ);
timerActive = true;
}
//stop the timer
//This resets it so you need to call setupTCTimer with prescaler and CC values before restarting
void stopTCTimer(){
resetTC(TCTimer);
timerActive = false;
}
Related
I am facing a strange issue while programming PIC18f45k22. I am initiating system modules and then I write a blocking delay for 3 seconds to wait for a touch screen to start (because I need to send some information to this screen). But when I do this I discovered that when I initiate timer before writing the delay, I discover that the system freezes.
This is the code that I used
NOTE: all the functions work fine without any problem.
void main(void) {
Sys_Init();
__delay_ms(3000);
while(1){
//Code
}
System initiation code
void Sys_Init(void) {
Delay_XTAL();
OSCILLATOR_Init();
DIGITAL_PIN_STATE();
ANALOG_PIN_STATE();
ADC_INIT(); //change clock select pins when oscillator changes
Spi_Init();
DHT22_init();
Uart_Init(9600); //initiate UART (change baud values when oscillator value changed)
Timer1_Init(); //initiate timer one (change prescaler and counter value if oscillator value changes)
}
Timer code
void Timer1_Init(void){ //for interrupt program use this function
//Prescaler 1:1; TMR1 Preload = 61536; Actual Interrupt Time : 1 ms
T1CON = 0x01;
TMR1IF = 0;
TMR1H = 0xF0;
TMR1L = 0x60;
TMR1IE = 1;
INTCON = 0xC0;
}
Interrupt code
void __interrupt() ISR (void){
Timer1();
}
void Timer1(void){ //for interrupt program
if (TMR1IF){
cnt1++;
TMR1H = 0xF0; //overflow every 1 ms
TMR1L = 0x60;
TMR1IF = 0;
}
}
Hi I write code below for initial External Interrupt for LPC2138 in KEIL 4.7 Compiler and when run code in proteus software , code dosent Work. I double check VIC and EXTINT registers seems correct. thanks
Project Picture on Proteus
one Switch on EXTINT2 (P0.15) and one LED on P1.25
#include <LPC213x.h>
void delay(int count);
void init_ext_interrupt(void);
__irq void Ext_ISR(void);
int main (void)
{
init_ext_interrupt(); // initialize the external interrupt
while (1)
{
}
}
void init_ext_interrupt(void) //Initialize Interrupt
{
EXTMODE = (1<<2); //Edge sensitive mode on EINT2
EXTPOLAR &= ~(1<<2); //Falling Edge Sensitive
PINSEL0 = 0x80000000; //Select Pin function P0.15 as EINT2
/* initialize the interrupt vector */
VICIntSelect &= ~(1<<16); // EINT2 selected as IRQ 16
VICVectAddr5 = (unsigned)Ext_ISR; // address of the ISR
VICVectCntl5 = (1<<5) | 16;
VICIntEnable = (1<<16); // EINT2 interrupt enabled
EXTINT &= ~(1<<2); //Set interrupt
}
__irq void Ext_ISR(void) // Interrupt Service Routine-ISR
{
IO1DIR |= (1<<25);
IO1SET |= (1<<25); // Turn ON LED
delay(100000);
IO1CLR |= (1<<25); // Turn OFF LED
EXTINT |= (1<<2); //clear interrupt
VICVectAddr = 0; // End of interrupt execution
}
void delay(int count)
{
int j=0,i=0;
for(j=0;j<count;j++)
{
for(i=0;i<35;i++);
}
}
You should correct the line:
(VICVectCntl5 = (1<<5) | 16;)
to:
(VICVectCntl5 = 0x20 | 16;)
as datasheet said.
I've been doing a project about home automation in which I have to use timer interrupts with 8051 microcontroller. I've constructed the following code, however I couldn't manage to get interrupt working. It seems that the program does not go into timer ISR at all. I use a buton to simulate PIR input, therefore lampControl is triggered, no worries there. I use as a library.
Any ideas or help will be greately appreciated:
void timer0_isr(void) interrupt 1 //Timer 0 Interrupt
{
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
if (++lamp_interrupt_count == 6000)
{
sendCharacterShowAsHex(0x8F);
lamp_interrupt_count = 0;
TR0 = 0;
}
}
void main()
{
unsigned char chr;
IE = 0x93;
while(1)
{
serialInput();
if (getPIRInput() == 0x00)
{
lampControl(0x80);
}
....
....
....
}
void lampControl(unsigned char serial_data_in)
{
if (serial_data_in == 0x80)
{
sendCharacterShowAsHex(0x80);
//enable interrupts
IE = 0x93;
device_interrupt = 2; //Lamp
TMOD = 0x21; // Timer0 Gate=0, Mode 1, 16bit timer
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1;
}
else if(serial_data_in == 0x8F)
{
sendCharacterShowAsHex(0x8F);
}
}
You need to configure the timer and interrupts before you can use them.
In main() you need at least the following configuration bits set in order to be able to turn
the timer on with "TR0 = 1;" :
Set those bits first thing in main() and this should do the trick:
TMOD = 0x01; // 16-bit no auto reload
TH0 = 0xDC; //Set high and low bits to count 0xFFFF - 0xDC00 = 0x23FF counts
TL0 = 0x00;
ET0 = 1; // Enable timer0 interrupt
EA = 1; // Enable all interrupts
//TR0 = 1; //Enable Timer0 immediately
The rest of your code should run fine.
Note: you could change your interrupt function definition to:
"void timer0_isr(void) interrupt 1 using 1" to force it to use register bank 1 for the interrupt function operation.
I am trying to enable timer2 interrupt to use it for PWM purposes. In this case, I just turn on an LED and when timer 2 interrupt occurs I turn it off but the timer interrupt never occurs. Everything looks good to me so I don't understand why Timer2 is not firing up. I am using PIC18F87J11, here is the datasheet.
/*
File: main.c
Date: 2011-SEP-4
Target: PIC18F87J11
IDE: MPLAB 8.76
Compiler: C18 3.40
*/
#include <p18cxxx.h>
#include<usart.h>
#include <pwm.h>
#include <delays.h>
#pragma config FOSC = INTOSC, WDTEN = OFF, XINST = OFF
#pragma interrupt HighISR
void main(void) {
unsigned int i;
/* set FOSC clock to 8MHZ */
OSCCON = 0b01110000;
/* turn off 4x PLL */
OSCTUNE = 0x00;
/* make all ADC inputs digital I/O */
ANCON0 = 0xFF;
ANCON1 = 0xFF;
PR2 = 124; // Period
TMR2=0;
// 1/16 prescalar
T2CONbits.T2CKPS0 = 1;
T2CONbits.T2CKPS1 = 0;
PIE1bits.TMR2IE == 1; // Enables the TMR2 to PR2 match interrupt
// Enable Timer 2
T2CONbits.TMR2ON = 1;
INTCONbits.PEIE = 1; // Enable Perpherial Interrupt
INTCONbits.GIE = 1; // Enable Global Interrupt
TRISDbits.TRISD6 = 0; // Turn on LED
LATDbits.LATD6 = 1;
while (1);
}
#pragma code highVector=0x08
void HighVector(void) {
_asm goto HighISR _endasm
}
#pragma code /* return to default code section */
// Timer Interrupt
void HighISR(void) {
if (PIR1bits.TMR2IF == 1) {
LATDbits.LATD6 = 0; // Turn off LED to indicate it came thru
PIR1bits.TMR2IF = 0;
}
}
Thanks!
Found my mistake
PIE1bits.TMR2IE == 1;
It should be PIE1bits.TMR2IE = 1;
I am working on lpc2468 ARM-based microcontroller. I am not able to configure the UART1 of the controller. Previously I was using UART0 for the same purpose, but due to some changes in hardware now I have to use UART1.
I want to connect GPRS module (SIMCOM 300) to the UART1 of the microcontroller. Here's my code so far:
void init_serial_gprs (void){
PINSEL7 |= 0x0000000F;
U1LCR = 0x00000080;
U1DLL = 0x00000041;
U1DLM = 0x00000000;
U1LCR = 0x03;
VICVectAddr7 = (unsigned)serial1_isr;
VICIntEnable = 1 << 7;
SBUF= U1RBR;
U1IER = 3;
}
void serial1_isr(void) __irq {
volatile char dummy;
volatile UWORD32 IIR1;
/*------------------------------------------------
Repeat while there is at least one interrupt source.
------------------------------------------------*/
while (((IIR1 = U1IIR) & 0x01) == 0)
{
switch (IIR1 & 0x0E)
{
case 0x06: /* Receive Line Status */
dummy = U1LSR; /* Just clear the interrupt source */
break;
case 0x04: /* Receive Data Available */
case 0x0C: /* Character Time-Out */
SBUF= U1RBR;
if(genflag.fl_m_psw || new_st == 0 ||new_st == 0x1f){
if(genflag.gprs_con) receive_data();
else
receive_data_gprs();
}
break;
case 0x02: /* THRE Interrupt */
if(genflag.gprs_con) transfer_data();
else transfer_data_gprs();
dummy = U1LSR;
break;
default:
break;
}
}
VICVectAddr = 0; /* Acknowledge Interrupt */
}
did you try polling the serial port first against a dumb terminal (hyperterm, minicom, putty) to make sure you have the basics then move on to interrupts?