I am a beginner coder learning on the MSP430FR4133.
I'm using a servo motor as part of my system, which is controlled by PWM.
The motor rotates across 180°, with a 1ms pulse being far right and 2ms pulse being far left in a 20ms period. Unfortunately I'm simply having no success operating it.
I have some very simple code that my intention was to simply force the motor to the right-most position:
WDTCTL = WDTPW | WDTHOLD;
P1DIR |= BIT7; //P1.7 Output
P1SEL0 |= BIT7; //P1.7 Select
TA0CCR0 = 20000-1; //Set Period to 20ms.
TA0CCTL1 = OUTMOD_7; //CCR1 Reset/Set
TA0CCR1 = 1000; //Set Duty Cycle to 1ms
TA0CTL = TASSEL_SMCLK | MC_UP | TACLR; //Use SMCLK in Up Mode
Unfortunately, the code isn't operating as I intended. The motor is supplied by a 5v supply, though the PWM signal from the MPS430 is only 3.3v. Could this also be an issue?
Thanks for any assisstance, and I can expand on anything I have not given enough information on.
Related
I would like to configure tim3 ch1 ch2 as encoder mode, I have the same code on tim2( it's also general purpose timer) and it's working good.
Maybe there's another bits should I set but I cant find them.
I was trying to configure this timer to work without any outputs, just generate interrupt after set period of time but it's not working as well.
//TIM2 CH1 PA0 CH2 PA1 AF1
//TIM3 CH1 PE2 CH2 PE3 AF2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN ;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOEEN;
GPIOA->MODER |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1;
GPIOE->MODER |= GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1;
GPIOA->AFR[0] |= 0X00000011;
GPIOE->AFR[0] |= 0X00002200;
TIM2->SMCR = TIM_SMCR_SMS_0;
TIM2->CCMR1 = TIM_CCMR1_CC1S_0|TIM_CCMR1_CC2S_0;
TIM2->ARR = 24;
TIM2->DIER = TIM_DIER_UIE;
TIM2->CR1= TIM_CR1_CEN;
TIM3->SMCR = TIM_SMCR_SMS_0 ;
TIM3->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0;
TIM3->ARR = 24;
TIM3->DIER = TIM_DIER_UIE;
TIM3->CR1= TIM_CR1_CEN ;
enter image description here
Set SMCR to 0
Your code sets both timers to encoder mode 1, see the description of the SMCR register in the reference manual.
0001: Encoder mode 1 - Counter counts up/down on TI1FP1 edge depending on TI2FP2 level.
In this mode, the timer counter is incremented or decremented by the signals on then CH1 and CH2 input, instead of the internal clock. There must be some other component on the board, or line noise when they are unconnected, that managed to trigger TIM2 a few times.
PE2 is connected to an output of another IC
Check the schematics in the board user manual. PE2 is connected to the DRDY output of the onboard accelerometer.
You can use the CubeMX tool to find available pins for TIM3. Select your board in the Board Selector screen, it will show that PE2 and PE3 are already connected to something.
Set TIM3 combined channels to encoder mode, it will assign some free pins to the timer. You can then hold down CTRL and click on the pin to see alternatives (they will blink in blue), and you can drag the pin assignments with the mouse.
Ok, I find a solution :)
If I assign TIM3 CH1 to PB4 and CH2 to PB5 it's work good, but I don't understand why, can someone explain it ?
I'm a beginner with programming AVR devices, in an attempt to move away from the inefficient _ms_delay() and _us_delay() blocking functions I've been trying program using the built in timers to control the timing of a basic LED flashing program with the CTC timer mode on a 16-bit timer. My goal is to make the LED flash at 2 Hz, on for 0.5s, off for 0.5s.
According to the ATMega328P datasheet, the freqency of a CTC output should be f_CTC = f_Clock/(2N(OCR1A+1), since my chip is a 328P Xplained mini, it's default CPU speed is 16 MHz, using the above formula, with N=64, the required OCR1A value to achieve my desired frequency should be 62499. With all of this in mind I wrote the following code:
#include <avr/io.h>
int main(void)
{
// Setup for timer TC1 (16-bit) in CTC mode to run at 2 Hz
TCCR1A = 0x00;
OCR1A = 62499; // Sets time resolution to 0.5 s
TCCR1B = 0x0b;
TCCR1C = 0x00;
// Set pin directions
PORTD &= ~(1<<PORTD6);
DDRD |= (1<<DDD6);
while (1)
{
if(TIFR1 & (1<<OCF1A))
{
PORTD ^= (1<<PORTD6);
}
TIFR1 |= (1<<OCF1A);
}
}
However, when I run the code the LED flashes at a frequency of 1 Hz, which I was able to time with my phone. Additionally, when I change OCR1A to 31249, which should increase the frequency to 4 Hz it seems to be flashing at 8 Hz, or on-and-off 4 times per second. I feel like I'm misunderstanding something about how CTC mode works, if someone could explain it simply to me, or any other issues with my code, I would be grateful.
I noticed one thing that could cause the issues you are seeing.
You are using the line TIFR1 |= (1<<OCF1A); to clear the OCF1A bit. You are running that line very frequently, so there is a high chance that when OCF1A gets set, your code just clears it immediately before the if statement can see that it was set. You have no control over when that bit gets set; it can happen at any point in your loop.
You should only clear OCF1A after verifying that it is 1, like this:
if (TIFR1 & (1 << OCF1A))
{
PORTD ^= (1 << PORTD6);
TIFR1 |= (1 << OCF1A);
}
I'm not gonna waste your time, and just post the code along with the explanation
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void){
sei(); //Enable interrupts
DDRB = (1 << PORTB3);//Set pin P3 as an output and other pins as inputs
//PORTB = 0xff;
DDRA = (1 << PORTA7);//Set pin A7 as an output and other pins as inputs
//PORTA = (1 << PORTA7);
TCCR0 = (1 << WGM00) | (1 << WGM01) | (1 << COM01);//Enable PWM, and configure Timer
TIMSK = (1 << TOIE0);//Enabling an interrupt
OCR0 = 255;//Setting comparison value for the Output compare unit
TCCR0 |= (0b110 << CS00);//Selecting the clock as the falling edge on a certain pin
while(1){
/*
* The portion of the code creates a square wave with a period of 39 us, which means that the falling edge occurs at a period of 78us, and since the output period of
* the PWM is 50Hz for a servo, that fits perfectly (1/(79*10^-6 * 256) ~ 50), but for some reason, the servo doesn't move...*/
PORTA ^= (1<< PORT7);
_delay_us(39);
}
}
So, what's the problem?? I don't really have an oscilloscope to measure the frequency, so don't ask me to do that, but a peculiar thing that I did notice was that the voltage across the servo power wires was 2.7V when it should've been 5V, but the power supply itself was supplying 5V, and this only happened when I connected the signal pin to the PWM pin, and it happened regardless of whether the 5V rail was connected to the servo or not... Any ideas on what the problem is??
Your PWM output has a 50% duty cycle, so the effective port output voltage is reduced from 5v to 2.5v when measured with a voltmeter. Assuming you are measuring the voltage against Ground, it won't make any difference if 5v power line is connected to the servo, but it will make a difference if the PWM signal is not connected.
If the servo is bidirectional, then it is possible that the 50% duty cycle keeps it stationary - try a different duty cycle, it looks as if you have hard coded the PWM period, inverting the output every half-cycle. Try something like
PORTA ^= (1<< PORT7);
_delay_us(28);
PORTA ^= (1<< PORT7);
_delay_us(50);
I'm new to microcontrollers, so I'm interested is it possible to set correct frequency mode on TCCR2 8bit timer?I need it to control a servo;
On TCCR1 I do smth like this:
DDRB |= (1<<1) | (1<<2);
ICR1 = 20000;
TCCR1A = (1<<WGM11)|(1<<COM1A1) | (1<<COM1B1);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
OCR1A = 1500;//middle pos
And it works fine, but it seems there is no "ICR2" register and no capture mode on this timer, so I don't understand how can I set frequency to control servo on PB3.Is there any onter ways to control servos?
Thanks!
Input Capture does not set a pin, so it can't be used to control a servo. ICR1 is usually used for input compare, but is also used for output compare on Timer 1 in some PWM modes.
PB3 is controlled by the output compare on Timer 2. You have do set it up similarly to what you have for Timer 1. The OCR2 register may be relevant to your needs.
I am having trouble setting up high speed PWM on my ATtiny85. I need to use the PCK, at a speed of 400 kHz. I believe that I have followed the data sheet correctly, but for some reason, the timer interrupt flags are not working.
If I program the device, the output of the corresponding pin is a constant 5 V.
If I comment out the PCK setup and use the system clock instead, the flags are correctly set and PWM works fine. The code is posted. Why aren't the flags setting and the PWM isn't working?
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
PORTB = 0; //Reset values on port B
// After setting up the timer counter,
// set the direction of the ports to output
DDRB |= (1<<PB1) | (1<<PB0); // Set the direction of PB1 to an output
// PLLCSR - PLL control and status register:
// PLL is a clock multiplier - multiplies system 8 MHz by 8 to 64 MHz
// PLL is enabled when:PLLE bit is enabled,
// CKSEL fuse is programmed to 0001. This clock is
// switched off in sleep modes!
PLLCSR |= (1<<PLLE); // PLL enable
// Wait until the PLOCK bit is enabled
// before allowing the PCK to be enabled
//WaitForPLOCK();
//unsigned int i = 0;
while ((PLLCSR & (1<<PLOCK)) == 0x00)
{
// Do nothing until plock bit is set
}
PLLCSR |= (1<<PCKE); // Enable asynchronous mode, sets PWM clock source
TCCR1 =
(1<<CTC1) | // Enable PWM
(1<<PWM1A) | // Set source to pck
(1<<(CS10)) | // Clear the pin when match with ocr1x
(1<<COM1A1);
GTCCR = (1<<PWM1B) | (1<<COM1B1);
// Set PWM TOP value - counter will count and reset
// after reaching this value
// OCR1C
// 400 kHz 159
// 450 kHz 141
// 500 kHz 127
OCR1C = 159;
// Enable Timer1 OVF interrupt
TIMSK = (1<<OCIE1A) | (1<<TOIE1);
sei();
// This should set the duty cycle to about 75%
OCR1A = 120;
The solution involved the CKDIV8 fuse. To program this fuse correctly however, HVSP "High Voltage Serial Programming" was required. After removing this fuse so that the device operated at 8 MHz, the PWM gave a 400 kHz output. I hope other people find this useful!
The errata (section 27 of the datasheet) states 'PLL will not lock under 6 MHz'. The only workaround listed is to set the clock to 6 MHz or higher.