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;
}
}
Related
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;
}
I wrote a simple led blinking code with hardware interrupt 0 of 8051.
When button is pressed it goes into interrupt service routine (ISR). After executing it should come back in main function but it is not coming.
This is my c code. Any positive reply will be appreciated.
sbit LED = P1^0;
void delay(int ms)
{
int i;
for(i=0;i<ms;i++)
{
TMOD = 0x01;
TH0 = 0xFC;
TL0 = 0x66;
TR0 = 1;
while(TF0==0);
TR0 = 0;
TF0 = 0;
}
}
void main(void)
{
P1 = 0x00;
/// P3 = 0x04;
IT0 = 1;
EX0 = 1;
EA = 1;
LED=1;
while(1)
{
LED=~LED;
delay(200);
}
return ;
}
void external0_isr() interrupt 0
{
EA=0;
LED =0 ;
delay(2000);
LED=1;
EX0=1;
EA=1;
return;
}
When you enter the button interrupt, you disable global interrupt EA=0;
That also disables the timer interrupt. Therefore, you program would hang at while(TF0==0) in your delay(2000) routine.
I'm trying to setup a timer that executes every 1ms, in order to estimate the execution time of a function. I attach the code at the end of the post. I'm pretty sure about the configuration of the timer, since I have done only very little modification to a working demo (iNemo_AHRS_continous demo, even the function for prvFindFactors is taken by such a demo).
Unfortunately, in this case my ISR is never executed, any idea of the reason? Thanks
void iNemoProfilerConfig(void)
{
unsigned short a;
unsigned short b;
unsigned long n;
/* This value is the frequency interrupts in Hz */
unsigned short frequency = 1000;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable timer clocks */
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM4, ENABLE );
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
TIM_TimeBaseStructInit( &TIM_TimeBaseStructure );
/* Time base configuration for timer 2 - which generates the interrupts. */
n = SystemCoreClock/frequency;
prvFindFactors( n, &a, &b );
TIM_TimeBaseStructure.TIM_Period = b - 1;
TIM_TimeBaseStructure.TIM_Prescaler = a - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM4, &TIM_TimeBaseStructure );
TIM_ARRPreloadConfig( TIM4, ENABLE );
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 13;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
}
void Enable_Timer4(FunctionalState command) {
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
TIM_ITConfig( TIM4, TIM_IT_Update, command );
}
void TIM4_IRQHandler(void)
{
USART1_Printf("INTERRUPT = %i \r\n", counter);
if(TIM_GetITStatus(TIM4, TIM_IT_Update))
{
//xTim2Raised=SET;
counter++;
/* Clear the IRQ bit */
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
//main chunk
int main(void)
{
SystemInit();
/* At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f10x_xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f10x.c file
*/
USART1_Init();
for(uint32_t i=0; i<0xFFFFF;i++);
iNemoTimerConfig();
iNemoProfilerConfig();
iNEMO_AHRS_Start();
LSM_Start();
L3G_Start();
/* Wait some seconds in order to ensure the user opens the VCOM terminal */
for(uint32_t i=0;i<0x1FFFFFF;i++);
Enable_Timer(ENABLE);
Enable_Timer4(ENABLE);
while(1)
{
counter = 0;
iNEMO_AHRS_Attitude();
USART1_Printf("counter = %i \r\n", counter);
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.
For last 2 weeks am trying to learn timer & interrupt & wrote a program (with my understanding) to blink LEDs on ATMEGA2560 but no matter what I do TCNT0 never increments & ISR() function never gets called. Where am I going wrong and how can I fix it? Here is my code:
#include<avr/io.h>
#include<avr/interrupt.h>
#define READ_ATMEGA(ADDR) *((P_CHAR)(BASE_ADDR + ((ADDR) * ADDR_MULTIPLIER)))
#define WRITE_ATMEGA(ADDR, DATA) *((P_CHAR)(BASE_ADDR + ((ADDR) * ADDR_MULTIPLIER))) = DATA
#define BASE_ADDR 0x20
void init_timer0_ovf_interrupt(void);
void timer0_interrupt_isr(void);
void initialize_ports(void);
void delay(unsigned int no_65_5ms_interrupts);
void __attribute__((ISR)) timer0_interrupt_isr(void);
//#pragma interrupt_handler timer0_interrupt_isr:24
unsigned int delay_timer;
int main(void)
{
initialize_ports();
init_timer0_ovf_interrupt();
delay(46);
return 0;
}
void initialize_ports(void)
{
READ_ATMEGA(4) = 0xff;
WRITE_ATMEGA(5, 0x00);
}
void delay(unsigned int no_65_5ms_interrupts)
{
TCNT0 = 0x00;
delay_timer = 0;
while(delay_timer <= no_65_5ms_interrupts)
{
;
}
}
void init_timer0_ovf_interrupt(void)
{
TCCR0A = 0X00;
TCCR0B = 0x02;
TIMSK0 = 0x01;
TIFR0 = 1<<0;
OCR0A = 25;
sei();
}
void timer0_interrupt_isr(void)
{
delay_timer++;
if(delay_timer >= OCR0A)
{
PORTB = ~(PORTB);
delay_timer = 0;
}
}
The global variable delay_timer is shared between interrupt and non-interrupt code. It should be declared as volatile as the value can change outside of delay().
If you look at the generated code for delay() you'll probably see that the value of delay_timer isn't being re-read while spinning in the while loop.
Also, volatile isn't enough. You've got non-interrupt code and interrupt code both writing to the same variable (delay_timer). You need to protect writes to the variable in non-interrupt code, there's a race-condition there. The easy/lazy way is to disable interrupts & restore them in the non-interrupt code.
(As for setting up your interrupts & starting your timer, that info should be in the chip's datasheet. Usually that's the part that's easier to get right, it's the shared data stuff that bites people.)
3-4 days ago, I wrote the same program a little differently & got LEDs blinking but still not sure whether it is the correct way of using timer & interrupt. Could anyone please see this & tell me whether it's the correct or not? I managed to write this program by reading programs of timers, interrupts.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t intrs;
ISR(TIMER0_OVF_vect) {
/* this ISR is called when TIMER0 overflows */
intrs++;
/* strobe PORTB.5 - the LED on arduino boards */
if (intrs >= 61){
PORTB = ~PORTB;
intrs = 0;
}
}
int main(void) {
TCCR0B = 0x02;
/* Enable Timer Overflow Interrupts */
TIMSK0 = 0x01;
/* other set up */
DDRB = 0xff;
TCNT0 = 0;
intrs = 0;
/* Enable Interrupts */
sei();
while (1)
; /* empty loop */
}
If it's the correct way then I can start working on next step.
Thanks
If could be that your while loop in the delay function doesn't do anything and will not increment delay_timer so you are stuck in an endless loop:
void delay(unsigned int no_65_5ms_interrupts)
{
TCNT0 = 0x00;
delay_timer = 0;
while(delay_timer <= no_65_5ms_interrupts)
{
; //Nothing is happening here!!
}
}