I'm trying to get my 28BYJ-48 Stepper Motor to rotate one quarter of a revolution and then stop, but I'm having trouble implementing that in code. What I find is no matter how small I make the numbers in my code or even alter it in ways that make me think it'd work properly, I can't get it to stop rotating. Below is the helpful part of the code.
#define STEPPER (*((volatile uint32_t *)0x4000703C))
// Move 1.8 degrees clockwise, delay is the time to wait after each step
void Stepper_CW(uint32_t delay) {
Pt = Pt->Next[clockwise]; // circular
STEPPER = Pt->Out; // step motor
if(Pos==199) { // shaft angle
Pos = 0; // reset
}
else {
Pos--; // CW
}
SysTick_Wait(delay);
}
// Move 1.8 degrees counterclockwise, delay is wait after each step
void Stepper_CCW(uint32_t delay) {
Pt = Pt->Next[counterclockwise]; // circular
STEPPER = Pt->Out; // step motor
if(Pos==0) { // shaft angle
Pos = 199; // reset
}
else {
Pos++; // CCW
}
SysTick_Wait(delay); // blind-cycle wait
}
// Initialize Stepper interface
void Stepper_Init(void) {
SYSCTL_RCGCGPIO_R |= 0x08; // 1) activate port D
SysTick_Init();
Pos = 0;
Pt = &fsm[0];
// 2) no need to unlock PD3-0
GPIO_PORTD_AMSEL_R &= ~0x0F; // 3) disable analog functionality on PD3-0
GPIO_PORTD_PCTL_R &= ~0x0000FFFF; // 4) GPIO configure PD3-0 as GPIO
GPIO_PORTD_DIR_R |= 0x0F; // 5) make PD3-0 out
GPIO_PORTD_AFSEL_R &= ~0x0F; // 6) disable alt funct on PD3-0
GPIO_PORTD_DR8R_R |= 0x0F; // enable 8 mA drive
GPIO_PORTD_DEN_R |= 0x0F; // 7) enable digital I/O on PD3-0
}
// Turn stepper motor to desired position
// (0 <= desired <= 199)
// time is the number of bus cycles to wait after each step
void Stepper_Seek(uint8_t desired, uint32_t time) {
short CWsteps;
if((CWsteps = (desired-Pos))<0) {
CWsteps+=200;
}
// CW steps is > 100
if(CWsteps > 100) {
while(desired != Pos) {
Stepper_CCW(time);
}
}
else {
while(desired != Pos) {
Stepper_CW(time);
}
}
}
Stepper_Seek gets called here...
#include <stdint.h>
#include "stepper.h"
#define T1ms 16000 // assumes using 16 MHz PIOSC (default setting for clock source)
int main(void) {
Stepper_Init();
Stepper_CW(T1ms); // Pos=1; GPIO_PORTD_DATA_R=9
Stepper_CW(T1ms); // Pos=2; GPIO_PORTD_DATA_R=5
Stepper_CW(T1ms); // Pos=3; GPIO_PORTD_DATA_R=6
Stepper_CW(T1ms); // Pos=4; GPIO_PORTD_DATA_R=10
Stepper_CW(T1ms); // Pos=5; GPIO_PORTD_DATA_R=9
Stepper_CW(T1ms); // Pos=6; GPIO_PORTD_DATA_R=5
Stepper_CW(T1ms); // Pos=7; GPIO_PORTD_DATA_R=6
Stepper_CW(T1ms); // Pos=8; GPIO_PORTD_DATA_R=10
Stepper_CW(T1ms); // Pos=9; GPIO_PORTD_DATA_R=9
Stepper_CCW(T1ms); // Pos=8; GPIO_PORTD_DATA_R=10
Stepper_CCW(T1ms); // Pos=7; GPIO_PORTD_DATA_R=6
Stepper_CCW(T1ms); // Pos=6; GPIO_PORTD_DATA_R=5
Stepper_CCW(T1ms); // Pos=5; GPIO_PORTD_DATA_R=9
Stepper_CCW(T1ms); // Pos=4; GPIO_PORTD_DATA_R=10
Stepper_Seek(8,T1ms);// Pos=8; GPIO_PORTD_DATA_R=10
Stepper_Seek(0,T1ms);// Pos=0; GPIO_PORTD_DATA_R=10
while(1) {
Stepper_CW(10*T1ms); // output every 10ms
}
}
I thought it could have been that it kept resetting its position and starting again after it reset but even after commenting out the line it wouldn't fix.
Thank you everyone in advance!
Your wrap-around logic is backward in both Stepper_CW() and Stepper_CCW(). Take the former as the example. Suppose you're trying to reach 198, and Pos is initially 1:
On the first call, Pos is decremented to 0. This is unequal to 198, so the function is called again.
On the second call, Pos is decremented to 199. This is unequal to 198, so the function is called again.
On the third call, the wrap-around case is triggered, and Pos is set to 0. This is unequal to 198 ....
The state after step (3) is the same as the state after step (1) -- infinite loop.
Related
I'm trying to create a blocking delay for the ATmega328p for my arduino uno R3. but I'm running into problems with my function.
I am not sure if it has something to do with my clock source. I set it as 8 MHz at the top of my script, but the problem persists.
#define F_CPU 8000000UL
Here's a snip of my delay function, it's configured for a 10 ms delay at 8 MHz and uses a delayTime counter to create a longer delay
/*
* Function to instantiate timer operations
*/
void makeDelay(int delayTime){
// Call the timer for delayTime successively
// resulting in delay of (configured delay) * timerDelay
while(delayTime > 0){
/* Set the TCNT reg for 10ms # 8 MHz */
TCNT0 = 0xB2;
// Define the mode and prescaler values
TCCR0A = 0x00; // Normal mode
TCCR0B = 0x05; // prescaler = 1024
// loop until overflow
while( (TIFR0 & (1 << TOV0) == 0) );
// Stop the timer
TCCR0B = 0;
// clear the overflow
TIFR0 = 0x01;
// decrement the counter
delayTime--;
}
}
Any advice would be appreciated.
In your program this waiting condition is wrong:
(TIFR0 & (1 << TOV0) == 0)
Operator == has higher precedence than &. (1 << TOV0) == 0 is evaluated first and it is always false. Thus TIFR0 & 0 is always false too.
Rewrite the condition as
((TIFR0 & (1 << TOV0)) == 0)
or
((TIFR0 & _BV(TOV0)) == 0)
You can use following formula to calculate TCCNT0 register value for milliseconds:
Nticks = 256 - ms*F_CPU/1000/prescaler
As Timer0 is used by Arduino runtime library, you should disable it at first.
Entire sketch may be like this:
#define MS2TICKS(ms) (256 - (ms)*(F_CPU/1000)/1024)
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
TCCR0B = 0; // Stop timer
TIMSK0 = 0; // Disable timer interrupts
TCCR0A = 0; // Normal mode
TIFR0 = ~0; // Clear all interrupt flags
}
void my_delay()
{
TCNT0 = MS2TICKS(10);
TCCR0B = 0x05; // Enable timer with prescaler 1024
while ((TIFR0 & _BV(TOV0)) == 0);
TCCR0B = 0; // Stop Timer0
TIFR0 = _BV(TOV0); // Clear overflow flag
}
void loop()
{
digitalWrite(LED_BUILTIN, HIGH);
my_delay();
digitalWrite(LED_BUILTIN, LOW);
my_delay();
}
It generates 10 ms pulses with 10 ms pauses on LED output.
My task is to take a functional program written by my instructor in assembly language and convert it to C language. The program is written for the TI MSP430G2553, and utilizes an attached 4-digit LED display with three side-by-side push-buttons. The display is supposed to start blank. When the right button is pressed, "ECE-3362" will begin to scroll across the display from right to left.
My conversion is 'complete' as far as I can tell and the code compiles with no errors. However, the push-buttons do not have any visible effect and the display remains blank.
I do get a few "Integer conversion resulted in truncation" warnings, but I'm not sure of the effect of this on the functionality of the program. At most I would assume this would cause the display to show the wrong values if part of my constant definitions get truncated.
One part of my code that is particularly suspicious to me is my port2 interrupt service routine at the very bottom (mainly the PB & P2_IFG comparisons). I didn't fully comprehend what was going on in my instructor's assembly version of that section, so something important may have been lost in translation.
I am new to assembly in general, and C as far as microcontrollers go. I was leaning heavily on my instructors example code as a reference. I'll put my code below, and I have my instructor's code on hand if anyone would like to see it.
I know the rules say not to post entire files but as far as I can tell I could have a mistake or more just about anywhere in the program.
My code:
#include <msp430g2553.h>
//-----------------------------------------------------------------------------
// Definition of Constants
//-----------------------------------------------------------------------------
#define TIMER_A0_COUNT_1 2000 //2000
#define TIMER_A1_COUNT_1 50000 //50000
#define MAX_TIMER_COUNT 10 //10
#define LONG_DELAY (0xFFFF) //65535
//definitions of segment positions
#define SEG_A (0x01u) // 00000001 Port pin position P1.0
#define SEG_B (0x02u) // 00000010 Port pin position P1.1
#define SEG_C (0x04u) // 00000100 Port pin position P1.2
#define SEG_D (0x08u) // 00001000 Port pin position P1.3
#define SEG_E (0x10u) // 00010000 Port pin position P1.4
#define SEG_F (0x20u) // 00100000 Port pin position P1.5
#define SEG_G (0x40u) // 01000000 Port pin position P1.6
#define SEG_DP (0x80u) // 10000000 Port pin position P1.7
//since inverted pattern is needed for the display, this defines inverse pattern
#define SEG_A_N ~SEG_A // Port pin position P1.0
#define SEG_B_N ~SEG_B // Port pin position P1.1
#define SEG_C_N ~SEG_C // Port pin position P1.2
#define SEG_D_N ~SEG_D // Port pin position P1.3
#define SEG_E_N ~SEG_E // Port pin position P1.4
#define SEG_F_N ~SEG_F // Port pin position P1.5
#define SEG_G_N ~SEG_G // Port pin position P1.6
#define SEG_DP_N ~SEG_DP // Port pin position P1.7
//NOTE: display board requires INVERSE of these patterns due to Active LOW
#define DIG_3 (0x01u) // 00000001 Port pin position P2.0 (MSdigit)
#define DIG_2 (0x02u) // 00000010 Port pin position P2.1
#define DIG_1 (0x04u) // 00000100 Port pin position P2.2
#define DIG_0 (0x08u) // 00001000 Port pin position P2.3(LSdigit)
#define DP_COM (0x10u) // 00010000 Port pin position P2.4
//since inverted pattern is needed for the display, this defines inverse pattern
#define DIG_3_N ~DIG_3 // Port pin position P2.0 (MSdigit)
#define DIG_2_N ~DIG_2 // Port pin position P2.1
#define DIG_1_N ~DIG_1 // Port pin position P2.2
#define DIG_0_N ~DIG_0 // Port pin position P2.3(LSdigit)
#define DP_COM_N ~DP_COM // Port pin position P2.4
//Pushbutton assignments CORRECTED to compensate for board layout swap
#define PB_0 (0x20u) // 00100000 Port pin position P2.5 RightMost button
#define PB_1 (0x80u) // 10000000 Port pin position P2.7 Middle button
#define PB_2 (0x40u) // 01000000 Port pin position P2.6 LeftMost button
#define SEG_PORT P1OUT
#define DIG_PORT P2OUT
#define PB_PORT P2IN
//NOTE: display bd requires the INVERSE of these patterns due to Active LOW
#define ONE (0x06u) // 00000110
#define TWO (0x5Bu) // 01011011
#define THREE (0x4Fu) // 01001111
#define FOUR (0x66u) // 01100110
#define FIVE (0x6Du) // 01101101
#define SIX (0x7Du) // 01111101
#define SEVEN (0x03u) // 00000111
#define EIGHT (0x7Fu) // 01111111
#define NINE (0x67u) // 01100111
#define ZERO (0x3Fu) // 00111111
//since inverted pattern is needed for the display, this defines inverse pattern
#define ONE_N (~0x06u) // ~00000110
#define TWO_N (~0x5Bu) // ~01011011
#define THREE_N (~0x4Fu) // ~01001111
#define FOUR_N (~0x66u) // ~01100110
#define FIVE_N (~0x6Du) // ~01101101
#define SIX_N (~0x7Du) // ~01111101
#define SEVEN_N (~0x03u) // ~00000111
#define EIGHT_N (~0x7Fu) // ~01111111
#define NINE_N (~0x67u) // ~01100111
#define ZERO_N (~0x3Fu) // ~00111111
//other figures for scrolling display
#define E_N (~0x79u) // ~01111001
#define C_N (~0x39u) // ~00111001
#define DASH_N (~0x40u) // ~01000000
#define BLANK_N (~0x00u) // ~00000000
//------------------------------------------------------------------------------
// Definition of Variables
//------------------------------------------------------------------------------
int DisplayValue = 0; // contains 4 digit value to display in BCD format
// BCDdig3 | BCDdig2 | BCDdig1 | BCDdig0
// xxxx xxxx xxxx xxxx
char CurrentDigitPos = 0; // global variable used by WriteDigitToDisplay ISR
// holds digit position of current digit to write
char CurrentDigitValue = 0; // global variable used by WriteDigitToDisplay ISR
// holds digit value of next digit to write
char StartFlag = 0; // Boolean state flags
char PauseFlag = 0;
char ContinueFlag = 0;
char ScrollingStateFlag = 0;
char PB_0_Mode = 0;
char PB_1_Mode = 0;
char PB_2_Mode = 0;
char Hundred_mS = 0;
char TotalINTCount1 = 0;
int PatternsToScroll[12] = {0x0000, 0x0006, 0x0065, 0x0656, 0x6561, 0x5613,
0x6133, 0x1334, 0x3342, 0x3420, 0x4200, 0x2000};
char PatternsIndex = 0;
int CurrentPattern = 0;
char PatternsLeft = 12;
char SegPatterns[7] = {BLANK_N, DASH_N, TWO_N, THREE_N, SIX_N, C_N, E_N};
//-----------------------------------------------------------------------------
// Functions
//-----------------------------------------------------------------------------
void WriteNextDigitToDisplay(int DisplayValue, char CurrentDigitPos, char CurrentDigitValue)
{
int DisplayValueCopy = 0; // initialize function variable
DIG_PORT |= DIG_0+DIG_1+DIG_2+DIG_3+DP_COM; // eliminate ghosting
if ((CurrentDigitPos - 0) == 0)
{
DisplayValueCopy = DisplayValue;
DisplayValueCopy &= 0x000F;
SEG_PORT = SegPatterns[DisplayValueCopy];
DIG_PORT = DIG_0_N;
CurrentDigitPos++;
}
if ((CurrentDigitPos - 1) == 0)
{
DisplayValueCopy = DisplayValue;
DisplayValueCopy &= 0x00F0;
DisplayValueCopy >>=4; //rra 4 times to get val into LSnibble
SEG_PORT = SegPatterns[DisplayValueCopy];
DIG_PORT = DIG_1_N;
CurrentDigitPos++;
}
if ((CurrentDigitPos - 2) == 0)
{
DisplayValueCopy = DisplayValue;
DisplayValueCopy &= 0x0F00;
DisplayValueCopy = __swap_bytes(DisplayValueCopy);
SEG_PORT = SegPatterns[DisplayValueCopy];
DIG_PORT = DIG_2_N;
CurrentDigitPos++;
}
if ((CurrentDigitPos - 3) == 0)
{
DisplayValueCopy = DisplayValue;
DisplayValueCopy &= 0xF000;
DisplayValueCopy = __swap_bytes(DisplayValueCopy);
DisplayValueCopy >>=4;
SEG_PORT = SegPatterns[DisplayValueCopy];
DIG_PORT = DIG_3_N;
CurrentDigitPos++;
}
if ((CurrentDigitPos - 4) == 0)
{
CurrentDigitPos = 0;
}
}
void delay()
{
for (int i = 0; i < LONG_DELAY; i++);
}
int main( void )
{
//---------------------------------------------------------------------------
// Setup
//---------------------------------------------------------------------------
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
// Setup Port 1 (all outputs for segment display)
P1DIR = SEG_A + SEG_B + SEG_C + SEG_D + SEG_E + SEG_F + SEG_G + SEG_DP;
// Setup Port 2
P2DIR = 0x1F; // (00011111 : 3MSbits as inputs (pushbuttons) 5LSbits as outputs)
P2OUT = PB_0 + PB_1 + PB_2; // 11100000 or 0xE0 defines pushbutton positions
P2REN |= PB_0 + PB_1 + PB_2; // turn on internal pull-up for the pushbuttons
// Activate the General Purpose Digital I/O mode for P2.6 and P2.7
P2SEL &= ~PB_1 + ~PB_2;
// Setup Port 2 interrupts for the pushbuttons
P2IE |= PB_0 + PB_1 + PB_2;
P2IES |= PB_0 + PB_1 + PB_2;
// Turn off all the segments and digits
SEG_PORT = 0xFF;
DIG_PORT = 0xFF;
// SetupCalibratedClock
// Set up the clock (calibrated mode at 1 MHz)
// Get the calibrated data for the DCO clock
// Set DCO to 1 MHz: (this directly from TI Family Guide page283 and 284
DCOCTL = 0; // Select lowest DCOx and MODx settings
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // Set DCO step + modulation
// Set up Timers
// TimerA0
TA0CCR0 = TIMER_A0_COUNT_1; // load a count "up to"value into timer
TA0CTL = TASSEL_2+ID_3 + MC_1; // SMCLK, input div = 8, upmode
TA0CCTL0 = CCIE; // interrupt enabled for Timer0
// TimerA1
TA1CCR0 = TIMER_A1_COUNT_1; // load a count "up to"value into timer
TA1CTL = TASSEL_2+ID_3 + MC_1; // SMCLK, input div = 8, upmode
TA1CCTL0 = CCIE; // interrupt enabled for Timer1
// Start of main program
// Initialize Boolean state flags and some other variables
StartFlag = 0;
PauseFlag = 0;
ContinueFlag = 0;
ScrollingStateFlag = 0;
PB_0_Mode = 0;
PB_1_Mode = 0;
PB_2_Mode = 0;
DisplayValue = 0;
// Clear Interrupt Flags
P1IFG = 0; // clear the Int flag register for Port 1
P2IFG = 0; // clear the Int flag register for Port 2
// Enable General Interrupts
_BIS_SR(GIE); // enable the general interrupts bit
//----------------------------------------------------------------------------
// Top of main program loop structure
//----------------------------------------------------------------------------
while(1) // forever loop
{
// test the Pushbutton mode Boolean variables to see what to do
if ((PB_0_Mode - 1) == 0) // (START CONDITION)
{
// Rightmost button (START)
PB_0_Mode = 0;
ScrollingStateFlag = 1; // make it TRUE
PatternsIndex = 0; // beginning of pattern array
PatternsLeft = 12;
CurrentPattern = PatternsToScroll[PatternsIndex]; // might be redundant
}
if ((PB_1_Mode - 1) == 0) // (CONTINUE CONDITION)
{
// Middle button (CONTINUE)
PB_1_Mode = 0;
ScrollingStateFlag = 1; // make it TRUE
}
if ((PB_2_Mode - 1) == 0) // (PAUSE CONDITION)
{
// Leftmost button (PAUSE)
PB_2_Mode = 0;
ScrollingStateFlag = 0; // make it FALSE
}
else
{
if ((ScrollingStateFlag - 1) == 0)
{
CurrentPattern = PatternsToScroll[PatternsIndex];
DisplayValue = CurrentPattern; // save pattern array element
PatternsIndex++; // move to next element
PatternsLeft--; // one less pattern to display
if ((PatternsLeft - 0) == 0) // done all the patterns --> reset variables
{
PatternsIndex = 0;
CurrentPattern = PatternsToScroll[PatternsIndex]; // might be redundant
PatternsLeft = 12;
}
delay(); // update the scrolling slowly
delay();
}
}
}
return 0;
} // end of MAIN
//------------------------------------------------------------------------------
// Subroutines
//------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// WriteNextDigitToDisplay
// passed in - DisplayValue, CurrentDigitPos
// returned - nothing
// accomplishes - Writes next digit to the expansion bd display
// uses: R15, global variable CurrentDigitPos, CurrentDigitValue
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// Interrupt Service Routines
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// Interrupt Service Routine for Timer_A 1
// Passed in: nothing
// Activated every time TimerA_1 times out
// Updates global variable TotalINTCount1 to keep track of number of TimerA_1
// interrupt events
// Uses: nothing except modifies global variable TotalINTCount
// For this example, set up to trigger every 100 mS
//-------------------------------------------------------------------------------
//Timer0_A0 ISR
#pragma vector=TIMER0_A0_VECTOR // this line tells the C compiler to put
// the start address of the following ISR
// into the Interupt Vector table
__interrupt void Timer_A0_ISR (void) // required syntax for first line of ISR
{
WriteNextDigitToDisplay(DisplayValue, CurrentDigitPos, CurrentDigitValue);
}
//Timer0_A1 ISR
#pragma vector=TIMER1_A1_VECTOR // this line tells the C compiler to put
// the start address of the following ISR
// into the Interupt Vector table
__interrupt void Timer_A1_ISR (void) // required syntax for first line of ISR
{
Hundred_mS++;
TotalINTCount1++;
}
// Port2_ISR
// passed in - nothing
// returned - nothing
// accomplishes - updates global Boolean variables for Pushbutton status
// uses: nothing
//-------------------------------------------------------------------------------
//Port2_ISR
// if we get to here, an interrupt occurred on the Port 2
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void)
{
if ((PB_0 & P2IFG) == 1)
{
PB_0_Mode |= 1;
PB_1_Mode &= ~1;
PB_2_Mode &= ~1;
}
if ((PB_1 & P2IFG) == 1)
{
PB_0_Mode &= ~1;
PB_1_Mode |= 1;
PB_2_Mode &= ~1;
}
if ((PB_2 & P2IFG) == 1)
{
PB_0_Mode &= ~1;
PB_1_Mode &= ~1;
PB_2_Mode |= 1;
}
P2IFG = 0;
}
Can't post instructor's entire code due to character limit. Here is his port 2 ISR:
; Port2_ISR
; passed in - nothing
; returned - nothing
; accomplishes - updates global Boolean variables for Pushbutton status
; uses: nothing
;-------------------------------------------------------------------------------
Port2_ISR
; if we get to here, an interrupt occurred on the Port 2
bit.b #PB_0, &P2IFG ; PB_0 Pushbutton? (if 1 it is pressed)
jnz PB_0_Case ; it is PB_0
;no so try the next case
bit.b #PB_1, &P2IFG ; PB_1 Pushbutton? (if 1 it is pressed)
jnz PB_1_Case ; it is PB_1
;no so try the next case
bit.b #PB_2, &P2IFG ; PB_2 Pushbutton? (if 1 it is pressed)
jnz PB_2_Case ; it is PB_2
jmp DoneWithPort_2_ISR ; no, so don't do anything
PB_0_Case
bis.b #1, &PB_0_Mode
bic.b #1, &PB_1_Mode ;clear other modes
bic.b #1, &PB_2_Mode
jmp DoneWithPort_2_ISR
PB_1_Case
bis.b #1, &PB_1_Mode
bic.b #1, &PB_0_Mode ;clear other modes
bic.b #1, &PB_2_Mode
jmp DoneWithPort_2_ISR
PB_2_Case
bis.b #1, &PB_2_Mode
bic.b #1, &PB_1_Mode ;clear other modes
bic.b #1, &PB_0_Mode
jmp DoneWithPort_2_ISR
DoneWithPort_2_ISR
clr.b &P2IFG ; clear the flag so system is ready for another interrupt
reti ; return from interrupt
;-------------------------------------------------------------------------------
; end of Port2_ISR
;-------------------------------------------------------------------------------
;-------------------------------------------------------------------------------
You have set all your PB_N_MODE to 0 and checking if subtracting 1 to those will equate to 0, can't you set them to 1 or perhaps check if they equate to -1?
PB_1_MODE = 1
PB_2_MODE = 1
...
#define PB_0 (0x20u)
if ((PB_0 & P2IFG) == 1)
The value of the expression PB_0 & P2IFG is either 0x20 or 0; it can never be 1.
All your ifs have the same structure (if ((x) == 0) or if ((x) == 1)), which is confusing and can lead to errors. You should treat the x properly as a boolean expression, and use if (x) to check for a non-zero value, or if (!(x)) for zero.
I am working on a project with the pic10f322 microcontroller. I've made a very basic communication protocol - there is a start pulse (10 ms) followed by a number of 5ms pulses (2 pulses - turns on a red light, 3 turns on yellow and 4 - green). So the following code is trying to read the communication protocol and turn on the respective light. I'm using TMR0 to measure the length of the pulse and count it. I have a bicolour LED (Red and Green) so I need to alternate the two to create the yellow. I was hoping to use TMR2 as an interrupt to allow me to pulse the yellow light separately from the rest of the code, so that it doesn't get in the way of my main function detecting start pulses.
I have no idea why it isn't working. I've checked the registers (although please do double check incase I'm blind to something). The code compiles.
I turned the light on at various stages of the code to check it, and the light turns on in every case statement, include the last one where I set the LedColour enum variable to the respective colour. When I try to turn the light on in the interrupt function, it never turns on.
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <pic.h>
#include <stdbool.h>
#include <pic10f322.h>
// crystal oscilator
define _XTAL_FREQ 1000000
// CONFIG
#pragma config FOSC = INTOSC // Oscillator Selection bits
#pragma config BOREN = OFF // Brown-out Reset disabled
#pragma config WDTE = OFF // WDT disabled
#pragma config PWRTE = OFF // PWRT disabled
#pragma config MCLRE = OFF // MCLR pin function
#pragma config CP = OFF // Code Protection disabled
#pragma config LVP = ON // Low-voltage programming enabled
#pragma config LPBOR = OFF // Brown-out Reset disabled
#pragma config BORV = LO // Brown-out Reset Voltage, low trip point
#pragma config WRT = OFF // Flash Memory Write protection off
void timer2_isr(void);
#pragma code high_vector=0x08;
void interrupt (void)
{
asm("GOTO timer2_isr");
}
#pragma code
#pragma interrupt timer2_isr
#define RED_LED 0x01
#define GREEN_LED 0x02
#define SetBit(bit) (PORTA |= bit )
#define ClearBit(bit) (PORTA &= ~bit)
#define TestBit(bit) ( PORTA&bit)
int clkval = 0;
int pulsecnt = 0;
enum {
Red,
Green,
Yellow,
Off,
} LedColor = Off;
void timer2_isr (void)
{
PORTA = 0b1101; //This turns a green light on if it enters this function
if (PIR1 == 0x02)
{
PIR1 = 0x00;
}
}
void main(int argc, char** argv)
{
OSCCON = 0x30; //1MHz Clk
TRISA = 0x0C;
ANSELA = 0x00;
PORTA = 0x0C;
OPTION_REG = 0x06;
T2CON = 0x04; //Timer2 Registers Prescaler= 1 - TMR2 PostScaler = 1 - PR2 = 254 - Freq = 980.39 Hz - Period = 0.001020 seconds
PIE1 = 0x02;
PIR1 = 0x00;
TMR0 = 0;
TMR2 = 0;
PR2 = 254;
INTCON = 0xC0;
__delay_ms(2000);
enum {
WaitForStart,
CountPulses,
SelectColor,
} State = WaitForStart;
while (1)
{
switch (State)
{
case WaitForStart: //wait for start pulse
if ( (PORTA & 0x04) != 0x04 )
{
TMR0 = 0;
while ((PORTA & 0x04) != 0x04)
{
clkval = TMR0;
}
if (18 < clkval < 22)
{
State = CountPulses;
pulsecnt = 0;
}
}
break;
case CountPulses: // found start pulse, now count pulses or reset
if ( (PORTA & 0x04) != 0x04 )
{
TMR0 = 0;
while ((PORTA & 0x04) != 0x04)
{
clkval = TMR0;
}
if (8 < clkval < 12)
{
pulsecnt++;
}
}
if ((PORTA & 0x04) == 0x04)
{
clkval = 0;
TMR0 = 0;
while ((PORTA & 0x04) == 0x04 && clkval < 45)
{
clkval = TMR0;
if ((44 < clkval) || (pulsecnt > 4)) //no pulses noticed in over 22ms comparison or if you have surpassed the max number of pulses you are supposed to reach
{
if (pulsecnt > 0)
{
State = SelectColor;
} //if there has been a long delay, and pulses have been detect (so pulsecnt is greater than 0) then move to next case
else
{
State = WaitForStart;
} // if long delay and no pulses have been detected, restart and check for start pulse again
}
}
}
break;
case SelectColor: // if pulses have been detected, this state will be visited after long delay ( >22ms)
if (pulsecnt == 2)
{
LedColor = Red;
//PORTA = 0b1110;
State = WaitForStart;
}
else if (pulsecnt == 3)
{
LedColor = Yellow;
State = WaitForStart;
}
else if (pulsecnt == 4)
{
LedColor = Green;
//PORTA = 0b1101;
State = WaitForStart;
}
else
{
LedColor = Off;
State = WaitForStart;
}
break;
default:
State = WaitForStart;
break;
}
}
}
I used "PORTA = 0b1101", which turns the green light on. as a test line to step through the code and make sure it's reach certain points. Right now it is at the beginning of the interrupt, so it should turn on and stay on righht after the first interrupt which would happen within approximately 2.5ms I think? Or relatively quickly anyway, but it never gets inside the interrupt function or the function before which uses assembly to tell it to goto this function.
In PIC16, you need to start the timer separately from the rest of the config, like that:
T2CONbits.TMR2ON = 1;
Also, check that you have enabled the interrupts:
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
I suggest using the above notation for initialization; what you have is hard to verify.
The PIC 10 has got only one interrupt vector and that is on address 0x04 (not 0x08 as you expected).
Try somethig like this:
void interrupt myISR(void)
{
........
}
This init function gets a TMR2 interrupting at high priority
on a PIC18F25K80
void pwm_mosfet_stepper_init() {
TMR2 = 0;
RCON |= (1<<7); // IPEN = 1;
PR2 = 100; // 100; // 100==#16uS 1.6ms
//
INTCON |= ( (1<<6) | (1<<7) ); // GIE/GIEH PEIE/GIEL i.e. both low and high enabled
// .......................... post4 ON pre4
//T2CON = 0x1D; // 2us tick ON 11 1 01
// .......................... post4 ON pre16
T2CON = 0x1F; // 16us tick ON 11 1 1X
IPR1 |= (1<<1); // TMR2IP = 1; HIGH PRIORITY
PIE1 |= (1<<1); // TMR2IE = 1; // interrupts are GO!
}
I am programming an embedded processor (Atmega644a) using C (Atmel studio) and I am trying to use this nested case/switch to cycle through my states but when I get to the 3rd state inside my "on" case, it doesn't go back to my startTrigger case, even though I set the current_state to startTrigger inside my rotate function, which I call in my startRotate state/case. Also, I am certain that all states are being entered and the functions work for the first cycle through the states. I've posted my code below, this is my first time posting on a forum so I'm not sure if you want the whole thing or not.
This is my full code
/*
* test_external_interrupts.c
*
* Created: 12/28/2017 10:49:37 AM
* Author : Celeste MacNeil
*/
//libraries
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include "UART_library.h"
// system constants
#define pi 3.14159265359
#define MAX 65535
#define ARRAYmax 11
const float fclk = 14.7456e6;
const float prescale = 1024;
const float cm_meter = 100;
const float speed_of_sound = 343;
const float fclk_echo = 14.7456e6;
const float ten_us = 10e-6;
const float prescale_echo = 8;
const float prescale_speaker = 256;
const float spkr_midpoint = 1.25;
// for input captures
volatile unsigned short current_edge;
volatile float rising_edge_timestamp, falling_edge_timestamp;
volatile float pulse_width;
// for distances
volatile float duration, distance, net_distance;
//counter
int p;
//states
enum fsm {startTrigger, startMeasure,startRotate};
enum fsm current_state;
enum status {on, off};
enum status button_status;
//distance/frequency values arrays and counter
float dist[] = {0,0.363636364,0.727272727,1.090909091,1.454545455,1.818181818,2.181818182,2.5454545,2.909090909,3.272727273,3.636363636,4};
float freq[] = {261.6, 282.7181818, 303.8363636,324.9545455,346.0727273,367.1909091,388.3090909,409.4272727,430.5454545,451.6636364,472.7818182,493.9};
// angle and pulse duration counts/arrays
float angle[] ={-90 ,-75,-60,-45,-30,-15, 0, 15, 30, 45, 60, 75, 90};
float pulseIn[]={0.00054,0.00065,0.0008,0.00094,0.0011,0.00125,0.0014,0.0016,0.0018,0.002,0.0022,0.0023,0.0024};
/******************************************************************************
function to trigger 10us pulse
************************************
******************************************/
void trigger(){
PORTA |=(1<<PA7);
/* initialize counter settings */
TCCR1A = 0; // not required since WGM11:0, both are pre-set to zero
TCCR1B |= (1<<WGM12)|(1<<CS11); // Mode = CTC, Prescaler = 8
TIMSK1 |= (1<<OCIE1A)|(1<<ICIE1); // set timer to reset when output compare is reached
TCNT1 = 0; // initialize counter
OCR1A = (int)fclk_echo*ten_us/prescale_echo; // initialize compare value
//printf("Trigger\n");
}
/******************************************************************************
When the timer count is reached, do something
******************************************************************************/
ISR (TIMER1_COMPA_vect)
{
PORTA &= ~(1<<PA7); //end 10us pulse on trigger pin
TCCR1B |= (1<<ICES1); //set input capture on rising edge
TIMSK1 |= (1<<ICIE1); //set input capture interrupt flag
TCCR1B |= (1 << WGM12)|(1 << CS11); // Mode = CTC, Prescaler = 8
TCNT1 = 0;
OCR1A = 0;
}
/******************************************************************************
When echo pulse is triggered, capture pulse duration
************************************
******************************************/
ISR (TIMER1_CAPT_vect)
{
if(current_edge == 1){
rising_edge_timestamp = ICR1; //save time stamp
TCCR1B &= ~(1<<ICES1); //set input capture on falling edge.
current_edge = 0;
}else if(current_edge == 0){
falling_edge_timestamp = ICR1;
TCCR1B |= (1<<ICES1); //set input capture to rising edge
current_edge = 1;
}
if(rising_edge_timestamp<falling_edge_timestamp){
pulse_width = falling_edge_timestamp - rising_edge_timestamp;
}else{
pulse_width = MAX - rising_edge_timestamp + falling_edge_timestamp;
}
//printf("rising edge: %f\n falling edge :%f\n pulse width: %f\n", rising_edge_timestamp, falling_edge_timestamp,pulse_width);
_delay_ms(100);
current_state = startMeasure;
}
/******************************************************************************
function to sound speaker tones
************************************
******************************************/
void speaker_tones(){
int q, r=0;
for(q=0;dist[q]<ARRAYmax;q++){
while(net_distance>dist[r]*cm_meter){
r++;
}
OCR0A = (int)((fclk_echo/(4*prescale_speaker*freq[r])));
//printf("%f",freq[r]);
_delay_ms(3000);
OCR0A = 0;
}
}
/******************************************************************************
function to measure echo pulse
************************************
******************************************/
void measure(){
duration = (pulse_width*prescale_echo)/fclk_echo;
distance = (duration*speed_of_sound*cm_meter)/2;
net_distance = sqrt((distance*distance -spkr_midpoint*spkr_midpoint));
if(distance>spkr_midpoint&&net_distance<395){
printf("The object distance is %.2f cm away.\n", net_distance);
printf("The object angle is %.2f degrees.\n\n", angle[p]);
speaker_tones();
printf("p in measure is %d\n\n",p);
}
_delay_ms(100);
p++;
current_state = startRotate;
}
/******************************************************************************
function to rotate servo
*************************************************
*****************************/
void rotate(){
if(p<=ARRAYmax){
printf("p in rotate is %d\n\n",p);
OCR2A = (int)(pulseIn[p]*fclk/prescale); // sets output compare to approx clockcycles per sample
}else{
p=0;
OCR2A = (int)(pulseIn[p]*fclk/prescale); // sets output compare to approx clockcycles per sample
}
_delay_ms(100);
current_state = startTrigger;
}
/******************************************************************************
read the button status on an external interrupt.
*************************************************
*****************************/
ISR (INT2_vect){
button_status^=button_status|1;
//printf("%d\n",button_status);
}
/******************************************************************************
function to power down or restart cycle
************************************
******************************************/
void zero_off(){
p=0;
OCR2A = (int)(pulseIn[p]*fclk/prescale); // sets output compare to approx clockcycles per sample
}
/******************************************************************************
main function:
***************************************
***************************************/
int main(void){
/* initialize output ports */
DDRB |= (1 << PB3); // Set PB3 (speaker) to write all others unchanged
DDRA |= (1<< PA7); // set A7 to write, all others are unchanged
DDRD |= (1<< PD7); // set PD7 to write, all others are unchanged
PORTB = 0;
PORTA = 0;
/*initialize counter settings*/
TIMSK1 = (1 << OCIE0A); // Enable compare interrupt
TCCR0A |= (1 << COM0A0 | 1 << WGM00); //pwm phase correct, toggle OC0A
TCCR0B |= (1 << CS02)|(1 << WGM02); //prescale set to 256
TCCR2A |= (1<<COM2A1)|(1<<WGM21)|(1<<WGM20);//clear OC2A on compare match fast PWM TOP = 0xFF
TCCR2B |= (1<<CS22)|(1<<CS21)|(1<<CS20); //set period of PWM to 17.7ms
TCNT0 = 0; //set bottom of counter to 0
/* enable external interrupt button setup */
EICRA |= (1<<ISC21)|(1<<ISC20); // set ISC2[1:0] to 0b01, so that the external
// interrupt on PB2 is sensitive to rising edge.
EIMSK |= (1<<INT2); // enable interrupts on PB2
/*initialize states*/
button_status = off;
current_state = startTrigger;
sei (); //global interrupt enabled
current_edge = 1; //set rising edge as first ICR timestamp
init_uart(); //call uart lib
while (1) // run indefinitely
{
switch(button_status){
case on:
switch(current_state){
case startTrigger:
trigger();
break;
case startMeasure:
measure();
break;
case startRotate:
rotate();
break;
}
break;
case off:
zero_off();
break;
}
}
}
I have reduced the code to a minimal size
Notice that the 'problem' switch is only stepped when a certain interrupt occurs, as handled by: ISR (TIMER1_CAPT_vect)
The probability is that interrupt is never being triggered.
And now, the shortened code:
//#include <avr/io.h>
//#include <avr/interrupt.h>
//#include <util/delay.h>
#include <stdio.h>
//#include "UART_library.h"
// note, following statement missing from posted code
#include <math.h> // M_PI, sqrt()
//prototypes
void trigger( void );
void measure( void );
void rotate( void );
//states
enum fsm {startTrigger, startMeasure,startRotate};
enum fsm current_state;
int triggered_interrupt;
int main( void )
{
/*initialize states*/
current_state = startTrigger;
while (1) // run indefinitely
{
if( triggeredInterrupt )
{
triggeredInterrupt = 0;
puts( "interrupt triggered" );
}
switch( current_state )
{
case startTrigger:
puts( "mode: startTrigger" );
trigger();
break;
case startMeasure:
puts( "mode: startMeasure" );
measure();
break;
case startRotate:
puts( "mode: startRotate" );
rotate();
break;
} // end switch currentState
} // end while forever
}
void trigger()
{
puts( "Trigger" );
}
void ISR (TIMER1_CAPT_vect)
{
current_state = startMeasure;
}
void measure()
{
puts( "measure" );
current_state = startRotate;
}
void rotate()
{
puts( "rotate" );
current_state = startTrigger;
}
I am trying to make a servo controller that have a higher resolution than the ATtiny85 8-bit timer/counter. So far I have managed to get about 2000 positions on my servo (1µs/step) within a time frame of 21'000 µs. I have also managed to move 5 servos sequential and with different speed, but now I want to move them synchronous.
My biggest problem is that I don't get how I should make it happen! I have looked around on other servo codes including servo8bit library and tried to find a way. It seems that most of the examples uses compare match ISR to move the servos "at the same time", my problem is that I have a 16-bit integer that I want to compare.
Is there a way to do some magic so I can use the 8-bit compare match ISR with my 16-bit integer? Or does anyone of you have some other suggestions on how I can move my servos synchronous without using compare match ISR?
I hope my questions make sense!
Since I don't really have any code to show yet (only flawed attempts without compar match ISR that makes no sense) I post the link to my TinyServo code if it helps.
EDIT 1:
Here is the part of the code I mentioned and didn't post the first time:
void servoMove(void)
{
uint16_t nextPulse = hPulse[0];
timerSetup (); //16-bit setup for counter
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if HIGH pulse (pos) is done
{
PORTB &= ~(1 << servo[channel]);
if (i+1 < sizeof(hPulse)/sizeof(hPulse[0]))
{
nextPulse += hPulse[i+1];
}
channel++;
}
else
{
channel = 0;
oTime = 0; //resets 16-bit variable
tot_overflow = 0; //resets tot_overflow variable
TIFR |= (1 << TOV1); // clear counter1 overflow-flag
TCNT1 = 0; //resets Timer/Counter1
}
}
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > tPulse - nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if LOW pulse (period) is done
{
PORTB |= (1 << servo[channel]);
nextPulse -= hPulse[i];
channel++;
}
}
}
void servoPosSet(volatile uint16_t pos[], uint8_t size)
{
for (i = 0; i < size; i++)
{
hPulse[i] = pos[i];
}
}
int main(void)
{
TCCR1 |= (1 << CS12); //set Timer/Counter1 prescaler to increment every 1 µs (PCK/8)
for (channel = 0; channel < size); channel++)
{
DDRB |= (1 << servo[channel]); //sets PB0-PB4 as output pins
}
channel = 0;
uint16_t pos[] = {2000, 1500, 1900, 1300, 1700};
uint8_t size = 5;
while(1)
{
servoPosSet(pos);
servoMove();
}
}
EDIT 2:
This is an illustration of how I think the code should work:
...but it does not!
If you have nothing else to do during the pulse, you could use a busy
loop instead of interrupts:
#include <avr/io.h>
#include <util/delay_basic.h>
/* Send a pulse of width = 4*count cycles. */
void pulse(uint16_t count, uint8_t channel)
{
uint8_t mask = 1 << channel,
old_port = PORTB,
high = old_port | mask,
low = old_port & ~mask;
PORTB = high;
_delay_loop_2(count);
PORTB = low;
}
This will give you a resolution of 4 clock cycles, or 0.5 µs with a
8 MHz clock.
Sending the pulses to the 5 servos should take at most 10 ms. Since
you repeat the pulse train every 21 ms, this leaves you 11 ms
to compute the next set of positions, which should be plenty. You could
program a timer to wake you up every 21 ms, then your main() may
look like:
int main(void)
{
static uint16_t pos[] = {4000, 3000, 3800, 2600, 3400};
uint8_t i;
/* Wake up every 21 ms. */
setup_timer();
sleep_enable();
for (;;) {
/* Update the servos. */
for (i = 0; i < 5; i++) pulse(pos[i], i);
/* Compute the next set of positions. */
...
/* Wait for timer interrupt. */
sleep_cpu();
}
}