I am new here and hoping to help and be helped.
My question is about systick interrupts on a tm4c123gh6pm m4 processor. When I use systick as timer (disable interrupt) it works properly, but when I enable interrupt, as the systick counter reaches the value 0, which should trigger the interrupt, it goes to IntDefaultHandler(void) which is in the startup.c file. I don't even enable global interrupt. But when I enable the global interrupt I am getting same result. I am sharing the code below, and please pay attention to the commented lines. By the way, I am using iar as my compiler.
#include <stdint.h>
#include <stdio.h>
#include "inc/tm4c123gh6pm.h"
volatile unsigned long Counts=0;
void SysTick_Init(unsigned long period){ // priority 2
NVIC_ST_CTRL_R = 0; // disable SysTick during setup
NVIC_ST_RELOAD_R = period-1;// reload value
NVIC_ST_CURRENT_R = 0; // any write to current clears it
NVIC_SYS_PRI3_R = (NVIC_SYS_PRI3_R&0x00FFFFFF)|0x40000000;
NVIC_ST_CTRL_R = 0x05; // Main problem is here, 0x05 means disable interrupt
// 0x07 means enable interrup. but 0x07 doesnt work for me
}
void SysTick_Handler(void){
GPIO_PORTF_DATA_R ^= 0x04; // toggle PF2
Counts = Counts + 1;
printf("interrupt working\n");
}
//*****************************************************************************
//
// Blink the on-board LED.
//
//*****************************************************************************
int
main(void)
{
volatile uint32_t ui32Loop;
unsigned long now;
//
// Enable the GPIO port that is used for the on-board LED.
//
SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;
//
// Do a dummy read to insert a few cycles after enabling the peripheral.
//
ui32Loop = SYSCTL_RCGC2_R;
//
// Enable the GPIO pin for the LED (PF2). Set the direction as output, and
// enable the GPIO pin for digital function.
//
GPIO_PORTF_DIR_R = 0x04;
GPIO_PORTF_DEN_R = 0x04;
SysTick_Init(16000); // initialize SysTick timer, every 1ms
//
// Loop forever.
//
while(1)
{ now = NVIC_ST_CURRENT_R; // get systick current value
printf("%u\n",now);
if((NVIC_ST_CTRL_R&0x00010000)!=0){ // check Systick flag
GPIO_PORTF_DATA_R ^= 0x04;
printf("timer working\n");
}
}
}
Edit: I have tried a working exapmle wrote by valvano, and this time it goes HardFault_Handler instead of SysTick_Handler. Code is so simple I don't understand the problem. Code is below.
// PeriodicSysTickInts.c
// Runs on LM4F120 or TM4C123
// Use the SysTick timer to request interrupts at a particular period.
// Daniel Valvano
// September 14, 2013
/* This example accompanies the book
"Embedded Systems: Introduction to ARM Cortex M Microcontrollers"
ISBN: 978-1469998749, Jonathan Valvano, copyright (c) 2013
Volume 1, Program 9.6
"Embedded Systems: Real Time Interfacing to ARM Cortex M Microcontrollers",
ISBN: 978-1463590154, Jonathan Valvano, copyright (c) 2013
Volume 2, Program 5.12, section 5.7
Copyright 2013 by Jonathan W. Valvano, valvano#mail.utexas.edu
You may use, edit, run or distribute this file
as long as the above copyright notice remains
THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED
OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
VALVANO SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
For more information about my classes, my research, and my books, see
http://users.ece.utexas.edu/~valvano/
*/
// oscilloscope or LED connected to PF2 for period measurement
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
#define GPIO_PORTF_DIR_R (*((volatile unsigned long *)0x40025400))
#define GPIO_PORTF_AFSEL_R (*((volatile unsigned long *)0x40025420))
#define GPIO_PORTF_DEN_R (*((volatile unsigned long *)0x4002551C))
#define GPIO_PORTF_AMSEL_R (*((volatile unsigned long *)0x40025528))
#define GPIO_PORTF_PCTL_R (*((volatile unsigned long *)0x4002552C))
#define SYSCTL_RCGC2_R (*((volatile unsigned long *)0x400FE108))
#define NVIC_SYS_PRI3_R (*((volatile unsigned long *)0xE000ED20)) // Sys. Handlers 12 to 15 Priority
#define NVIC_ST_CTRL_R (*((volatile unsigned long *)0xE000E010))
#define NVIC_ST_RELOAD_R (*((volatile unsigned long *)0xE000E014))
#define NVIC_ST_CURRENT_R (*((volatile unsigned long *)0xE000E018))
void DisableInterrupts(void); // Disable interrupts
void EnableInterrupts(void); // Enable interrupts
long StartCritical (void); // previous I bit, disable interrupts
void EndCritical(long sr); // restore I bit to previous value
void WaitForInterrupt(void); // low power mode
volatile unsigned long Counts = 0;
// **************SysTick_Init*********************
// Initialize SysTick periodic interrupts
// Input: interrupt period
// Units of period are 62.5ns (assuming 16 MHz clock)
// Maximum is 2^24-1
// Minimum is determined by length of ISR
// Output: none
void SysTick_Init(unsigned long period){
NVIC_ST_CTRL_R = 0; // disable SysTick during setup
NVIC_ST_RELOAD_R = period-1;// reload value
NVIC_ST_CURRENT_R = 0; // any write to current clears it
NVIC_SYS_PRI3_R = (NVIC_SYS_PRI3_R&0x00FFFFFF)|0x40000000; // priority 2
// enable SysTick with core clock and interrupts
NVIC_ST_CTRL_R = 0x07;
EnableInterrupts();
}
// Interrupt service routine
// Executed every 62.5ns*(period)
void SysTick_Handler(void){
GPIO_PORTF_DATA_R ^= 0x04; // toggle PF2
Counts = Counts + 1;
}
int main(void){
SYSCTL_RCGC2_R |= 0x00000020; // activate port F
Counts = 0;
GPIO_PORTF_DIR_R |= 0x04; // make PF2 output (PF2 built-in LED)
GPIO_PORTF_AFSEL_R &= ~0x04;// disable alt funct on PF2
GPIO_PORTF_DEN_R |= 0x04; // enable digital I/O on PF2
// configure PF2 as GPIO
GPIO_PORTF_PCTL_R = (GPIO_PORTF_PCTL_R&0xFFFFF0FF)+0x00000000;
GPIO_PORTF_AMSEL_R = 0; // disable analog functionality on PF
SysTick_Init(16000); // initialize SysTick timer
EnableInterrupts();
while(1){ // interrupts every 1ms, 500 Hz flash
WaitForInterrupt();
}
}
and all project files SysTickInt
For your first comment, that the interrupt default handler gets called, this is due to the fact that you did not declare an interrupt handler for the systick interrupt within the startup file. You can do it for example in the following way in the startup file.
extern void systick_isr(void); // somewhere you have to define this interrupt handler
Furthermore you have to add the function name of your interrupt handler to the vector table (excerpt below) in the startup file. Every line represents a certain interrupt handler, take a look at the comments, they tell you which one you have to adapt. For example you can see that the GPIOA interrupt handler will call the default interrupt handler at the moment. The same happens for you, at least for the first part of your question.
//*****************************************************************************
//
// The vector table. Note that the proper constructs must be placed on this to
// ensure that it ends up at physical address 0x0000.0000 or at the start of
// the program if located at a start address other than 0.
//
//*****************************************************************************
#pragma DATA_SECTION(g_pfnVectors, ".intvecs")
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((uint32_t)&__STACK_TOP),
// The initial stack pointer
ResetISR, // The reset handler
NmiSR, // The NMI handler
FaultISR, // The hard fault handler
IntDefaultHandler, // The MPU fault handler
IntDefaultHandler, // The bus fault handler
IntDefaultHandler, // The usage fault handler
0, // Reserved
0, // Reserved
0, // Reserved
0, // Reserved
IntDefaultHandler, // SVCall handler
IntDefaultHandler, // Debug monitor handler
0, // Reserved
IntDefaultHandler, // The PendSV handler
systick_isr, // The SysTick handler
IntDefaultHandler, // GPIO Port A
IntDefaultHandler, // GPIO Port B
IntDefaultHandler, // GPIO Port C
IntDefaultHandler, // GPIO Port D
...
Regarding the comment in your own code, how the interrupt can be enabled or disabled please be aware that this does not seem to be right. Enabling or disabling the systick interrupt can be achieved by setting the bit number one of the STCTRL register accordingly. The picture below shows the register. To enable the interrupt you have to set the bit number one high (NVIC_ST_CTRL_R |= 0x02;) to disable the interrupt you have to clear the bit (NVIC_ST_CTRL_R &= ~0x02;)
Related
I am trying to implement a 16-bit timer overflow interrupt on the ATMEGA168. The idea is to write a message to the UART I/O register when the timer overflows.
I've tested the UART separately and it works fine via RealTerm (baudrate of 9600 bits/s).
I created a base project from https://start.atmel.com/#dashboard where I had to set the input clock frequency to 16MHz to be compatible with the debugger (see page 5). So I would expect to see a 0x1 on my serial terminal every (16x106 / 1024)-1 x 216 = 4.194 seconds.
However, I'm not seeing anything on the terminal regardless of the prescaler I select. Can anyone please advise what could be going wrong?
I have attached the ISR and the main() below:
#include <atmel_start.h>
#include <stdio.h>
#include <usart_basic.h>
#include <atomic.h>
#include <avr/interrupt.h>
#include <avr/io.h>
// timer1 overflow
ISR(TIMER1_OVF_vect) {
// Send 0x1 over UART
UDR0 = 0x1;
}
int main(void) {
atmel_start_init();
// enable timer overflow interrupt for Timer1
TIMSK1 = (1<<TOIE1); // also tried |=
// start 16-bit counter with /1024 prescaler
TCCR1B = (1 << CS10) | (1 << CS12); // also tried |=
TCCR1A = 0x0;
// enable interrupts
sei();
while(true) {
// more code here...
}
}
I have tried to isolate the problem by not writing to UART in the ISR, but just incrementing a counter (declared with the volatile qualifier) and then printing its value to the screen via UART in the while(true) loop. But the counter doesn't increment either and remains stuck at 0.
You have no USART initialisation code. Specifically you don't enable the transmitter or set the baud rate. I accept that you have tried it with a counter, but that is not the code shown so we can come to no conclusion about its correctness or otherwise.
Without initialisation, the transmitter will not run, and the baud rate will be 1Mbps. Your need at least:
// Set baud rate 9600
uint16_t brr = (FOSC / 16 / 9600) - 1
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)ubrr;
// Enable transmitter
UCSR0B = (1<<TXEN0);
// Note reset state frame is N,8,1
I am not convinced that it matters but your timer initialisation order is not idiomatic. You would normally enable the interrupt after setting the prescaler and any other configurations, and to ensure the first period is a complete period, reset the counter to zero immediately before enabling interrupts.
// set up timer with prescaler = 1024
TCCR1B = (1 << CS12) & (1 << CS11);
// initialise counter
TCNT1 = 0;
// enable overflow interrupt
TIMSK = (1 << TOIE1);
// enable global interrupts
sei();
As I said, I am not sure that will fix your problem but the elided part:
while(true) {
// more code here...
}
may well be the code that is breaking it. You would do well to discount that possibility by disabling or removing any code there temporarily.
There is Attiny85, with an internal clock source at 8 MHz.
I am trying to implement a microsecond timer based on the hardware timer timer0.
What is my logic:
Since the clock frequency is 8 MHz and the prescaler is off, the time of one clock cycle will be about 0.1us (1/8000000).
Initially, the timer overflows and causes interruptions when passing 0 ... 255, it takes more than 0.1us and is inconvenient for calculating 1μs.
To solve this, I thought about the option to change the initial value of the timer instead of 0 to 245. It turns out that in order to get to the interruption, you need to go through 10 clock cycles, which takes about 1us in time.
I load this code, but the Attiny LED obviously does not switch for about 5 seconds, although the code indicates 1 second (1000000us).
Code:
#include <avr/io.h>
#undef F_CPU
#define F_CPU 8000000UL
#include <avr/interrupt.h>
// Timer0 init
void timer0_Init() {
cli();
//SREG &= ~(1 << 7);
// Enable interrupt for timer0 overflow
TIMSK |= (1 << 1);
// Enabled timer0 (not prescaler) - CS02..CS00 = 001
TCCR0B = 0;
TCCR0B |= (1 << 0);
// Clear timer0 counter
TCNT0 = 245;
sei();
//SREG |= (1 << 7);
}
// timer0 overflow interrupt
// 1us interval logic:
// MCU frequency = 8mHz (8000000Hz), not prescaler
// 1 tick = 1/8000000 = 100ns = 0.1us, counter up++ after 1 tick (0.1us)
// 1us timer = 10 tick's => 245..255
static unsigned long microsecondsTimer;
ISR(TIMER0_OVF_vect) {
microsecondsTimer++;
TCNT0 = 245;
}
// Millis
/*unsigned long timerMillis() {
return microsecondsTimer / 1000;
}*/
void ledBlink() {
static unsigned long blinkTimer;
static int ledState;
// 10000us = 0.01s
// 1000000us = 1s
if(microsecondsTimer - blinkTimer >= 1000000) {
if(!ledState) {
PORTB |= (1 << 3); // HIGH
} else {
PORTB &= ~(1 << 3); // LOW
}
ledState = !ledState;
blinkTimer = microsecondsTimer;
}
}
int main(void)
{
// Set LED pin to OUTPUT mode
DDRB |= (1 << 3);
timer0_Init();
while (1)
{
ledBlink();
}
}
Attiny85 Datasheet
What could be the mistake? I have not yet learned how to work with fuses, so I initially loaded the fuses at 8 MHz through the Arduino IDE, and after that I already downloaded the main code (without changing the fuses) through AVRDUDE and Atmel Studio.
And another question, should I check the maximum value when updating my microsecond counter? I know that in Arduino, the micro and millis counters are reset when they reach the maximum value. For example, if I do not clear the TimerMicrosecond variables variable and it exceeds the size of the unsigned long, will it crash?
As pointed out by #ReAI, your ISR does not have enough time to run. Your ISR will take more than 1 microsecond to execute and return, so you always are missing interrupts.
There are other problems here too. For example, your microsecondsTimer variable is accessed in both the ISR and the foreground and is a long. long variables are 4 bytes wide and so are not updated atomically. It is possible, for example, that your foreground could start reading the value for microsecondsTimer and then in the middle of the read, the ISR could update some of the unread bytes, and then when the foreground starts again it will end up with a mangled value. Also, you should avoid messing with the count register since updating it can miss ticks unless you are very careful.
So how could you implement a working uSec timer? Firstly you'd like to call the ISR as infrequently as possible, so maybe pick the largest prescaller you can get get the resolution that you want and only ISR on overflow. In the case of the ATTINY85 Timer0, you can pick /8 prescaller which gets you one tick of the timer per microsecond with an 8Mhz system clock. Now your ISR only runs once every 256 microseconds and when it runs, it need only increment a "microseconds * 256" counter in each call.
Now to read the current microseconds in the foreground, you can get the number of microseconds mod 256 by directly reading the count register, and then read the "microseconds * 256" counter and multiply this by 256 and add that the counter and you'll have the full count. Note that you will need take special precautions to make sure your reads are atomic. You can do this either by carefully turning off the interrupts, quickly reading the values, and then turning the interrupts back on (save all the math for when interrupts are back on), or looping on the read values to make sure you get two full reads in a row that are the same (time means that have not updated while you were reading them).
Note that you can check out the source code to Arduino timer ISR for some insights, but note that theirs is more complicated because it can handle a wide range of tick speeds whereas you are able to keep things simple by specifically picking a 1us period.
why you didn't use pre-scaler ?!
your code need a relly relly big delay intervall(1 sec it's huge time according to cpu speed) .... so it's not wisdom choose to interrupt microcontroller every 1 us !!.. so it will be great if we could slow down your microcontroller clock and make interrupt for example every 1 ms
calculation
the microcontroller clock speed is 8 mega Hz so if we chose the preScaller to 64 then the timer clock will be 8MHz/64=125 KHz so that mean each tik (timer clock) time will be 1/125KHZ=8 us
so if we like to have inturrpt every 1ms then we need 125 tik
modify code
try this code it's more clear to understand
#undef F_CPU
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int millSec;
void timer0_Init();
void toggleLed();
int main(void)
{
// Set LED pin to OUTPUT mode
DDRB |= (1 << 3);
timer0_Init();
millSec = 0; // init the millsecond
sei(); // set Global Interrupt Enable
while (1)
{
if(millSec >= 1000){
// this block of code will run every 1 sec
millSec =0; // start count for the new sec
toggleLed(); // just toggle the led state
}
// Do other backGround jobs
}
}
//#####Helper functions###########
void timer0_Init() {
// Clear timer0 counter
TCNT0 = 130; //255-125=130
// Enable interrupt for timer0 overflow
TIMSK = (1 << 1);
// set prescaler to 64 and start the timer
TCCR0B = (1<<CS00)|(1<<CS01);
}
void toggleLed(){
PORTB ^= (1 << 3); // toggle led output
}
ISR(TIMER0_OVF_vect) {
// this interrupt will happen every 1 ms
millSec++;
// Clear timer0 counter
TCNT0 = 130;
}
Sorry, i am late but i have got some suggestions. If you calculate the Timer0 with prescaler 1, the timer is counting up every 125ns. It is not possible to reach 1 us without a small divergence. But if you use prescaler 8 you reach exactly 1 us. I actually do not have your hardware but give this a try:
#ifndef F_CPU
#define F_CPU 8000000UL
#else
#error "F_CPU already defined"
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
volatile unsigned int microsecondsTimer;
// Interrupt for Timer0 Compare Match A
ISR(TIMER0_COMPA_vect)
{
microsecondsTimer++;
}
// Timer0 init
void timer0_Init()
{
// Timer0:
// - Mode: CTC
// - Prescaler: /8
TCCR0A = (1<<WGM01);
TCCR0B = (1<<CS01);
OCR0A = 1;
TIMSK = (1<<OCIE0A)
sei();
}
void ledBlink() {
static unsigned int blinkTimer;
if(microsecondsTimer >= 1000)
{
microsecondsTimer = 0;
blinkTimer++;
}
if(blinkTimer >= 1000)
{
PORTB ^= (1<<PINB3);
blinkTimer = 0;
}
}
int main(void)
{
// Set LED pin to OUTPUT mode
DDRB |= (1 << PINB3);
timer0_Init();
while (1)
{
ledBlink();
}
}
If you are using internal clock of attiny it may be divied by 8. To disable the clock division you have to disable the prescaler within 4 clock cycles (atomic operation):
int main(void)
{
// Reset clock prescaling
CLKPR = (1<<CLKPR);
CLKPR = 0x00;
// ...
Please try this solution an give feedback if it is working. Maybe you can verify it with an oscilloscope...
Notice that operations with unsigned long needs more than 1 clock cycle to handle on an 8 bit microcontroller. Maybe it would be better to use unsigned int or unsigned char. The main loop also should not contain lots if instructions. Otherwise error correction of microsecond timer has to be implemented.
for 4 days now, I am struggling to set up External interrupt on my STM32 and I have gone through tons of reading and other people's code to get it. But no luck.
I have two buttons and when pressing either one of them I expect to light up an LED, this example is only to get it working, I wanted to have something functional before proceeding and building rest of the code.
I am sorry if the code is a little messy, but I am working on neating my coding skills.
I've gone through manuals and datasheets but nothing seems to help.
Here is my main.c
#include "stm32l1xx_hal.h"
#include "buttons.h"
static void MX_GPIO_Init(void);
bool RightButtonFlag = 0;
bool LeftButtonFlag = 0;
int main(void)
{
HAL_Init();
SystemClock_Config();
controls_Interrupt_Init();
MX_GPIO_Init();
while (1)
{
if(RightButtonFlag){
Blue_ON();
}
if(LeftButtonFlag){
Green_ON();
}
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
{
Followed by part of stm32l1xx_it.c
#include "stm32l1xx_hal.h"
#include "stm32l1xx.h"
#include "stm32l1xx_it.h"
#include "buttons.h"
extern volatile uint8_t RightButtonFlag;
extern volatile uint8_t RightButtonFlag;
void EXTI15_10_IRQHandler(void)
{
if(GPIOC->IDR & GPIO_IDR_IDR_10){
RightButtonFlag = 1;
EXTI->PR |= EXTI_PR_PR10;
}
if(GPIOC->IDR & GPIO_IDR_IDR_11){
LeftButtonFlag = 1;
EXTI->PR |= EXTI_PR_PR11;
}
}
buttons.c
void controls_Interrupt_Init(void){
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; /* Enable System Configuration Register */
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI11_PC; /* Set up External Interrupt for Pin 11 Port C */
SYSCFG->EXTICR[3] |= SYSCFG_EXTICR3_EXTI10_PC; /* Set up External Interrupt for Pin 10 Port C */
EXTI->IMR |= EXTI_IMR_MR11;
EXTI->IMR |= EXTI_IMR_MR10;
EXTI->FTSR |= EXTI_FTSR_TR11; /* Falling trigger Selection Reg. Trigger 11 */
EXTI->FTSR |= EXTI_FTSR_TR10; /* Falling trigger Selection Reg. Trigger 10 */
NVIC_SetPriority(EXTI15_10_IRQn,1); /* Set Interrupt priority for pins 10-15 */
NVIC_EnableIRQ(EXTI15_10_IRQn); /* Enable NVIC for Pins Between 10-15 */
}
and buttons.h
void controls_Interrupt_Init(void);
#define Blue_ON() (HAL_GPIO_WritePin(BLUE_LED_PORT, BLUE_LED_PIN, 1))
#define Green_ON() (HAL_GPIO_WritePin(GREEN_LED_PORT, GREEN_LED_PIN, 1))
I am fairly new to coding and with my poor experience I expect to have screwed up something very simple.
You have to declare the flags as volatile. That's a hint to the compiler that the variable can change any time, independent of the normal program flow.
volatile bool RightButtonFlag = 0;
volatile bool LeftButtonFlag = 0;
When there is no volatile there, the compiler can assume that the flags never change in the main loop, and optimize it that way, that the variables will be loaded only once, before the loop.
Declaring them volatile only in the scope of the interrupt handler does no good, because the optimization will still take place in main(), where the volatile declaration is not visible.
It is good practice to move the declaration to a header file, and include that header everywhere the variable is referenced. Then the compiler can check that the types are indeed compatible.
UPDATE
The interrupt handler makes little sense. You set EXTI to detect a falling edge, then check if the input is high. Checking the GPIO data register is not reliable anyway, due to the bouncing effect of mechanical buttons.
You should rather check EXTI->PR in the handler, and reset the pending bit with a simple assignment instead of |=, otherwise you could accidentally clear another pending bit too.
void EXTI15_10_IRQHandler(void)
{
if(EXTI->PR & EXTI_PR_PR10){
RightButtonFlag = 1;
EXTI->PR = EXTI_PR_PR10;
}
if(EXTI->PR & EXTI_PR_11){
LeftButtonFlag = 1;
EXTI->PR = EXTI_PR_PR11;
}
}
You can still check somewhere if GPIOC->IDR actually reflects the button state, to eliminate possible hardware problems.
You can also try setting EXTI->SWIER from the debugger, or in the code, to simulate a button press.
Hello i have been coding with Tm4c123gh6pm lately and i have faced a part where i want to use the edge timing mode , now i haven't went for the tivaware api's i just went for traditional register level and following the datasheet and i faced this problem
i want to know which pin to use as the one where the timer is waiting for the rising edge to come so it moves the current timer value ?
i checked the data sheet all i can find is that timer 0 is somehow related to PB6 and PF0 now i tried PB6 and it didn't work but is that even the correct approach ?
are these the correct pins the microcontroller's timer waits for the rising edge to move the current timer value ?
here is a sample of my code and please note that this is just a testing code im trying its not a final code what i did was copy the initialization section in the datasheet for the timer mode i want and followed it step by step
this code will make the counter start running from 0xFF downwards but when i place a 'high' signal i.e 3.3 volts on PB6 nothing is moved to the GPTMTnR register.
i just wonder if there is something im doing wrong and im not noticing ?
#include "tm4c123gh6pm.h"
void Timer0Init(void);
int main(void)
{
int i=0;
Timer0Init();
while(1){
for(i=0;i<100000;i++){
}
}
return 0;
}
void Timer0Init(void)
{
//initialize PORT B
volatile unsigned long delay;
SYSCTL_RCGC2_R |= 0x00000002; // 1) B clock
delay = SYSCTL_RCGC2_R; // delay to allow clock to stabilize
GPIO_PORTB_AMSEL_R &= 0x00; // 2) disable analog function
GPIO_PORTB_PCTL_R &= 0x00000000; // 3) GPIO clear bit PCTL
GPIO_PORTB_DIR_R &= 0x00; // 4.2) PB all input
GPIO_PORTB_AFSEL_R &= 0x40; // 5) no alternate function
GPIO_PORTB_DEN_R |= 0xFF; // 7) enable digital pins PF4-PF1
GPIO_PORTB_PCTL_R = 7;
//timer clock
SYSCTL_RCGCTIMER_R |=0x01;
delay = SYSCTL_RCGCTIMER_R;
//1. Ensure the timer is disabled (the TnEN bit is cleared) before making any changes.
TIMER0_CTL_R &= 0xFE;
//2. Write the GPTM Configuration (GPTMCFG) register with a value of 0x0000.0004.
TIMER0_CFG_R= 0x00000004;
//3. In the GPTM Timer Mode (GPTMTnMR) register, write the TnCMR field to 0x1 and the TnMR field to 0x3.
TIMER0_TAMR_R= TIMER0_TAMR_R | 0x007;
//4. Configure the type of event that the timer captures by writing the TnEVENT field of the GPTM Control (GPTMCTL) register.
TIMER0_CTL_R = TIMER0_CTL_R & 0xFFFFFFF3;
//5. If a prescaler is to be used, write the prescale value to the GPTM Timer n Prescale Register (GPTMTnPR).
//no prescaler for now
//6. Load the timer start value into the GPTM Timer n Interval Load (GPTMTnILR) register.
TIMER0_TAILR_R = 0xFF;
//7. If interrupts are required, set the CnEIM bit in the GPTM Interrupt Mask (GPTMIMR) register.
//no interrupts required
//8. Set the TnEN bit in the GPTM Control (GPTMCTL) register to enable the timer and start counting.
TIMER0_CTL_R= TIMER0_CTL_R & 0x00000001;
TIMER0_CTL_R |= 0x01;
//9. Poll the CnERIS bit in the GPTMRIS register or wait for the interrupt to be generated (if enabled). In both cases,
//the status flags are cleared by writing a 1 to the CnECINT bit of the GPTM Interrupt Clear (GPTMICR) register.
//The time at which the event happened can be obtained by reading the GPTM Timer n (GPTMTnR) register.
/*In Input Edge Timing mode, the timer continues running after an edge event has been detected,
but the timer interval can be changed at any time by writing the GPTMTnILR register. The change
takes effect at the next cycle after the write.*/
}
there was a problem with my portb initialization i used tivaware for that part and it worked perfectly fine now on placing high on PB6 it passes the current timer value to the other register
also note that when counting up the timer counts up to the value initialized in the TAIL_R register also if u want to reset the timer you need to write the value u want the timer to begin with in the TAV_R register
Best Of Luck everyone
I'm developing a C application using avr-libc on an AVR ATmega328P microcontroller. Since I don't have an ICE debugger for it, I followed these instructions and this tutorial for making the stdio.h functions such as printf able to use the hardware UART as stdout.
That works, and I can see the output on a PC terminal connected to my target board, but the strange thing is: When I have only one printf on main, but before the main loop something is causing the processor to reset, while if I have a printf only inside the main loop or before the main loop AND inside the loop it works fine. Something like this:
#include <stdio.h>
/* stream definitions for UART input/output */
FILE uart_output = FDEV_SETUP_STREAM(uart_drv_send_byte, NULL, _FDEV_SETUP_WRITE);
FILE uart_input = FDEV_SETUP_STREAM(NULL, uart_drv_read_byte, _FDEV_SETUP_READ);
int main() {
/* Definition of stdout and stdin */
stdout = &uart_output;
stdin = &uart_input;
/* Configures Timer1 for generating a compare interrupt each 1ms (1kHz) */
timer_init()
/* UART initialization */
uart_drv_start(UBRRH_VALUE, UBRRL_VALUE, USE_2X, &PORTB, 2);
/* Sets the sleep mode to idle */
set_sleep_mode(SLEEP_MODE_IDLE);
printf("START ");
/* main loop */
while(1) {
printf("LOOP ");
/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
sleep_mode();
}
}
The code above produces the following output:
START LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is expected. If we comment the printf("START ") line it produces this:
LOOP LOOP LOOP LOOP LOOP LOOP LOOP ... LOOP
which is also fine. The problem is, if I don't have any printf inside the while loop, it goes like this:
START START START START START START ... START
That clearly shows the processor is being restarted, since the expected output would be just one START and nothing else while the infinite loop goes on being awaken only on the 1 kHz timer interrupts. Why is this happening? I should stress there's no watchdog timer configured (if there was, the cases where only LOOP is printed would be interrupted by a new START also).
Monitoring execution using GPIO pins
To try to get some insight into the situation, I turned GPIO pins ON and OFF around the problematic print("START ") and sleep_mode in the main loop:
int main() {
/* Irrelevant parts suppressed... */
GPIO1_ON;
printf("START ");
GPIO1_OFF;
/* Main loop */
while(1) {
/* Sleeps so the main loop iterates only on interrupts (avoids busy loop) */
GPIO2_ON;
sleep_mode();
GPIO2_OFF;
}
}
It turned out that GPIO1 stays ON for 132 µs (printf("START ") call time) and then OFF for 6.6 ms - roughly the time to transmit the six characters at 9600 bit/s - and GPIO2 toggles 12 times (six times two interrupts: the UART-ready-to-transmit interrupt and the UART-empty-data-register interrupt), showing sleep active for another 1.4 ms before GPIO1 goes ON again indicating a new printf("START ") - hence after reset. I'll probably have to check out the UART code, but I'm pretty sure the non-interrupt UART version also shows the same problem, and that doesn't explain either why having a printf inside the main loop works OK, without a reset happening (I would expect the reset would happen in any case should the UART code be faulty).
(SOLVED!): For completeness, The UART init and TX code is below**
This was my first attempt in writing an interrupt driven UART driver for the AVR, but one that could be used either on a RS-232 or a RS-485, which requires activating a TX_ENABLE pin while transmitting data. It turned out that, since I had to make the code useable either on ATmega328P or ATmega644, the interrupt vectors have different names, so I used a #define TX_VECTOR to assume the right name according to the processor used. In the process of making and testing the driver the choosing of "TX_VECTOR" for the UDRE data empty interrupt ended up masking the fact I hadn't defined the USART0_TX_vect yet (this was work in progress, I might not even need both anyway...)
Right now I just defined an empty interrupt service routine (ISR) for USART0_TX_vect and the thing doesn't reset anymore, showing #PeterGibson nailed it right on. Thanks a lot!
// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_VECTOR USART_RX_vect
#define TX_VECTOR USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_VECTOR USART0_RX_vect
#define TX_VECTOR USART0_UDRE_vect
#endif
ISR(TX_VECTOR)
{
uint8_t byte;
if (!ringbuffer_read_byte(&txrb, &byte)) {
/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}
/* If RS-485 mode is on and the interrupt was called with TXC0 set it
* means transmission is over. TX_ENABLED should be cleared. */
if ((TX_ENABLE_PORT) && (UCSR0A & _BV(TXC0) & _BV(UDR0))) {
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
UCSR0B &= ~_BV(UDRIE0);
}
}
void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);
/* Disables UART */
UCSR0B = 0x00;
/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);
/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;
/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);
/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
}
FIXED UART CODE (NOW WORKING 100%!)
In order to help anyone interested or developing a similar interrupt driven UART driver for the AVR ATmega, here it goes the code with the problems above fixed and tested. Thanks to everyone who helped me spot the problem with the missing ISR!
// Interrupt vectors for Atmega328P
#if defined(__AVR_ATmega328P__)
#define RX_BYTE_AVAILABLE USART_RX_vect
#define TX_FRAME_ENDED USART_TX_vect
#define TX_DATA_REGISTER_EMPTY USART_UDRE_vect
// Interrupt vectors for Atmega644
#elif defined(__AVR_ATmega644P__)
#define RX_BYTE_AVAILABLE USART0_RX_vect
#define TX_FRAME_ENDED USART0_TX_vect
#define TX_DATA_REGISTER_EMPTY USART0_UDRE_vect
#endif
/* I/O port containing the pin to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t* TX_ENABLE_PORT = NULL;
/** Pin from the I/O port to be used as TX_ENABLE for the RS-485 driver */
static volatile uint8_t TX_ENABLE_PIN = 0;
ISR(RX_BYTE_AVAILABLE)
{
// Read the status and RX registers.
uint8_t status = UCSR0A;
// Framing error - treat as EOF.
if (status & _BV(FE0)) {
/* TODO: increment statistics */
}
// Overrun or parity error.
if (status & (_BV(DOR0) | _BV(UPE0))) {
/* TODO: increment statistics */
}
ringbuffer_write_byte(&rxrb, UDR0);
}
ISR(TX_FRAME_ENDED)
{
/* The end of frame interrupt will be enabled only when in RS-485 mode, so
* there is no need to test, just turn off the TX_ENABLE pin */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
}
ISR(TX_DATA_REGISTER_EMPTY)
{
uint8_t byte;
if (!ringbuffer_read_byte(&txrb, &byte)) {
/* If RS-485 is enabled, sets TX_ENABLE high */
if (TX_ENABLE_PORT)
*TX_ENABLE_PORT |= _BV(TX_ENABLE_PIN);
UDR0 = byte;
}
else {
/* No more chars to be read from ringbuffer, disables empty
* data register interrupt */
UCSR0B &= ~_BV(UDRIE0);
}
}
void uart_drv_start(uint8_t ubrrh, uint8_t ubrrl, uint8_t use2x,
volatile uint8_t* rs485_tx_enable_io_port,
uint8_t rs485_tx_enable_io_pin)
{
/* Initializes TX and RX ring buffers */
ringbuffer_init(&txrb, &tx_buffer[0], UART_TX_BUFSIZE);
ringbuffer_init(&rxrb, &rx_buffer[0], UART_RX_BUFSIZE);
cli();
/* Disables UART */
UCSR0B = 0x00;
/* Initializes baud rate */
UBRR0H = ubrrh;
UBRR0L = ubrrl;
if (use2x)
UCSR0A |= _BV(U2X0);
else
UCSR0A &= ~_BV(U2X0);
/* Configures async 8N1 operation */
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
/* If a port was specified for a pin to be used as a RS-485 driver TX_ENABLE,
* configures the pin as output and enables the TX data register empty
* interrupt so it gets disabled in the end of transmission */
if (rs485_tx_enable_io_port) {
TX_ENABLE_PORT = rs485_tx_enable_io_port;
TX_ENABLE_PIN = rs485_tx_enable_io_pin;
/* Configures the RS-485 driver as an output (on the datasheet the data
* direction register is always on the byte preceding the I/O port addr) */
*(TX_ENABLE_PORT-1) |= _BV(TX_ENABLE_PIN);
/* Clears TX_ENABLE pin (active high) */
*TX_ENABLE_PORT &= ~_BV(TX_ENABLE_PIN);
/* Enables end of transmission interrupt */
UCSR0B = _BV(TXCIE0);
}
/* Enables receptor, transmitter and RX complete interrupts */
UCSR0B |= _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
sei();
}
void uart_drv_send_byte(uint8_t byte, FILE *stream)
{
if (byte == '\n') {
uart_drv_send_byte('\r', stream);
}
uint8_t sreg = SREG;
cli();
/* Write byte to the ring buffer, blocking while it is full */
while(ringbuffer_write_byte(&txrb, byte)) {
/* Enable interrupts to allow emptying a full buffer */
SREG = sreg;
_NOP();
sreg = SREG;
cli();
}
/* Enables empty data register interrupt */
UCSR0B |= _BV(UDRIE0);
SREG = sreg;
}
uint8_t uart_drv_read_byte(FILE *stream)
{
uint8_t byte;
uint8_t sreg = SREG;
cli();
ringbuffer_read_byte(&rxrb, &byte);
SREG = sreg;
return byte;
}
You've possibly enabled the UDRE (Uart Data Register Empty) interrupt and not set a vector for it, so when the interrupt triggers the processor resets (according to the defaults). When printf is called continuously in the main loop, this interrupt is never triggered.
From the docs
Catch-all interrupt vector
If an unexpected interrupt occurs (interrupt is enabled and no handler
is installed, which usually indicates a bug), then the default action
is to reset the device by jumping to the reset vector. You can
override this by supplying a function named BADISR_vect which should
be defined with ISR() as such. (The name BADISR_vect is actually an
alias for __vector_default. The latter must be used inside assembly
code in case is not included.)
I ran in the same situation right now, but since I don't have a high reputation on stackoverflow, I can not vote.
here is a snippet of my initialization procedure that caused this problem to me:
void USART_Init()
{
cli();
/* Set baud rate */
UBRR0H = (uint8_t)(BAUD_PRESCALE>>8);
UBRR0L = (uint8_t)BAUD_PRESCALE;
/* Enable receiver and transmitter */
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
/* Set frame format: 8data, 1stop bit 8N1 => 86uS for a byte*/
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
/*enable Rx and Tx Interrupts*/
UCSR0B |= (1 << RXCIE0) | (1 << TXCIE0); //<- this was the problem
/*initialize the RingBuffer*/
RingBuffer_Init(&RxBuffer);
sei();
}
The problem was that I initially used interrupt based transmission, but later on I have changed the design and went for 10ms polling for Tx sequence, and forgotten to change this line as well in the init procedure.
Thanks very much for pointing this out Peter Gibson.