STM32F4 Encoder count is changing when it should not - microcontroller

I am currently using the STM32F4 with the STM32F429ZI Nucleo-144 Board. I am looking to use this microcontroller to evaluate the position of a rotary encoder via a quadrature encoder interface. Looking at the documentation, this is done with the timers. I have the A/B encoder outputs hooked up to PA6 and PC7 on the micro, but I have noticed that the counts appear to be drifting.
During the debugging, I noticed that if I disconnect one of the encoder outputs to the microcontroller and I move the motor, the counts still increment/decrement even though only one of the encoder lines are connected. Since I am counting on both the TI1 and TI2 edges, this should not be happening. If I am reading the below diagram correctly, since one of my lines is held high using the internal pull-up, clock pulses on the other input should be going up/down/up/down and really just cycling between two different counts. However, if I am rotating the encoder, the counts keep incrementing or decrementing depending on the direction.
Why is the encoder count changing with only one encoder input connected? I also have the scope trace attached to prove that only one count is is active, as well as the code.
EDIT: I have also tried changing the polarity from BOTH EDGE to RISING EDGE, with no perceived benefit.
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer_InitStruct;
TIM_Encoder_InitTypeDef Encoder_InitStruct;
void EncoderTest_Init()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer_InitStruct.Instance = TIM3;
Timer_InitStruct.Init.Period = 0xFFFF;
Timer_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer_InitStruct.Init.Prescaler = 1;
Timer_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
Encoder_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
Encoder_InitStruct.IC1Filter = 0x00;
Encoder_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC1Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
Encoder_InitStruct.IC2Filter = 0x00;
Encoder_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_BOTHEDGE;
Encoder_InitStruct.IC2Prescaler = TIM_ICPSC_DIV1;
Encoder_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer_InitStruct, &Encoder_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer_InitStruct);
}

Upon further investigation, it appears that the issue is due to the prescaler. The prescaler does not work in encoder mode when you provide even values. Since prescaler is the entered value + 1, using the STM32F4 HAL, the entered prescaler must be even.
I found confirmation that I am not the only person with this issue at this forum post. There is some discussion at the post that prescalers may not be compatible with encoder mode, but this has not yet been confirmed. I have sent an email to ST to get to the bottom of it. It is safe to enter a prescaler value of 0 if it is not supported.
Here is the working code below:
#include "stm32f4xx_hal.h"
#include "encoder_test.h"
GPIO_InitTypeDef GPIO_InitStruct;
TIM_HandleTypeDef Timer3_InitStruct;
TIM_Encoder_InitTypeDef EncoderTim3_InitStruct;
void EncoderTest_Init_Tim3()
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
/**TIM3 GPIO Configuration
PA6 ------> TIM3_CH1
PC7 ------> TIM3_CH2
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
Timer3_InitStruct.Instance = TIM3;
Timer3_InitStruct.Init.Period = 0xFFFF;
Timer3_InitStruct.Init.CounterMode = TIM_COUNTERMODE_UP;
Timer3_InitStruct.Init.Prescaler = 10;
Timer3_InitStruct.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
EncoderTim3_InitStruct.EncoderMode = TIM_ENCODERMODE_TI12;
EncoderTim3_InitStruct.IC1Filter = 0x00;
EncoderTim3_InitStruct.IC1Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC1Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC1Selection = TIM_ICSELECTION_DIRECTTI;
EncoderTim3_InitStruct.IC2Filter = 0x00;
EncoderTim3_InitStruct.IC2Polarity = TIM_INPUTCHANNELPOLARITY_RISING;
EncoderTim3_InitStruct.IC2Prescaler = TIM_ICPSC_DIV4;
EncoderTim3_InitStruct.IC2Selection = TIM_ICSELECTION_DIRECTTI;
if (HAL_TIM_Encoder_Init(&Timer3_InitStruct, &EncoderTim3_InitStruct) != HAL_OK)
{
while (1);
}
if (HAL_TIM_Encoder_Start_IT(&Timer3_InitStruct, TIM_CHANNEL_1) != HAL_OK)
{
while (1);
}
}
void TIM3_IRQHandler()
{
HAL_TIM_IRQHandler(&Timer3_InitStruct);
}
EDIT:
After speaking with ST tech support, the encoder interface was not intended to be used with a prescaler value, even OR odd. I have pasted their response below, but even with using a prescaler value that appears to work, it seems possible that the encoder counts drift over time.
Another solution is to use no prescalers, but instead extend the 16 bit value into the 32 bit space using the approach suggested here. I have reprinted the approach here in case the link goes dead:
From user goosen.kobus.001 on 11/19/2013 on ST's forum:
In my experience using an overflow interrupt to scale up an encoder is not reliable, especially when you have high resolution encoders: it happens from time to time that the encoder value will change sign in the instant you enter the interrupt, causing the system to increment the upper word when it should have decremented etc. This is especially true if the encoder is supposed to be stalled at 0, like a servo motor commanded to go to encoder position 0.
The best approach I have found is to do it manually. this is my procedure:
Ensure that the control loop that reads the encoder value is run often, (i.e. that if your encoder is rotating at full speed, the encoder value is still read at least 10-20 times between overflows. For my servo motor application a 1ms loop interval was sufficient.
keep track of the last read encoder value.
divide the current and last encoder value into quadrants (the most significant 2 bits). i.e. pos_now &= 0xC000; pos_last &= 0xC000;
check to see if the encoder has moved from quadrant 0 to quadrant 3 or 3 to 0 in the last step:
4.1 if(pos_now == 0 && pos_last == 0xC000) upper_word++;
4.2 if(pos_now == 0xC000 && pos_last == 0) upper_word--;
this is why I say the encoder read loop needs to be run often; you need to be sure that the value is read often enough that it is impossible to go from quadrant 0->1->2->3 in between reads.
It should also be possible to put this logic in another timer interrupt that runs at say 10kHz. that way you have an encoder value which is always up to date.
ST RESPONSE:
Hi,
I have got the feedback from the design and the architect of the timers.
The encoder interface has been designed to work without prescaler in order to not downgrade the resolution of the encoder.
As you observed, they have confirmed that it cannot work with even prescaler value but only the odd ones.
We have a sub-counter for the prescaler which is mono-directional, so not affected by the counter direction and incremented on every rising edge of the timer clock (without the prescaler).
The counter direction is updated on every rising edge of the timer clock (without the prescaler) but the counter is incremented only while the sub-counter of the prescaler reach the programmed value and according to the value in the direction bit.
So, in one case, the behavior is the same as without prescaler, because the counter is updated with a different direction (every odd clock cycles number), but in the other case, the direction is always the same when the counter is updated and the encoder interface doesn't work correctly.
So you can use the prescaler but with an odd value.
The recommended use case is without prescaler.
Best regards
ST MCU Tech Support

Related

Need help displaying serial monitor on LCD

I'm having issues displaying the serial monitor on an lcd. I am not getting any error and the LCD is lit up so I don't think I wired it wrong. I am able to open up the serial monitor/plotter and see in information changing so my other component is also working so the problem must be in the code...
#include <LiquidCrystal.h>
/**
* LIDARLite I2C Example
* Author: Garmin
* Modified by: Shawn Hymel (SparkFun Electronics)
* Date: June 29, 2017
*
* Read distance from LIDAR-Lite v3 over I2C
*
* See the Operation Manual for wiring diagrams and more information:
* http://static.garmin.com/pumac/LIDAR_Lite_v3_Operation_Manual_and_Technical_Specifications.pdf
*/
#include <Wire.h>
#include <LIDARLite.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Globals
LIDARLite lidarLite;
int cal_cnt = 0;
void setup()
{
Serial.begin(9600); // Initialize serial connection to display distance readings
lidarLite.begin(0, true); // Set configuration to default and I2C to 400 kHz
lidarLite.configure(0); // Change this number to try out alternate configurations
lcd.begin(16, 2);
// initialize the serial communications:
}
void loop()
{
int dist;
// At the beginning of every 100 readings,
// take a measurement with receiver bias correction
if ( cal_cnt == 0 ) {
dist = lidarLite.distance(); // With bias correction
} else {
dist = lidarLite.distance(false); // Without bias correction
}
// Increment reading counter
cal_cnt++;
cal_cnt = cal_cnt % 100;
// Display distance
Serial.print(dist);
Serial.println(" cm");
delay(10);
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
// clear the screen
lcd.clear();
// read all the available characters
while (Serial.available() > 0) {
// display each character to the LCD
lcd.write(Serial.read());
}
}
}
The LCD should be displaying the changing measurements
The LCD is lit up and I can adjust the back light but I can't get anything to show up.
Just because the LCD is "lit" doesn't mean it's wired correctly. In fact, the backlighting circuit is usually totally separate from the data and control signals circuits. I would start by checking the assumption that it's wired correctly with a simple command to print a known value to the LCD:
lcd.clear();
lcd.println("TEST");
If this works, then you know the LCD is working and can look elsewhere for the problem.
If this doesn't work, I'd question your assumption that it's hooked up correctly, but if you still get nothing but "blue blocks" then it might be something as simple as your contrast is not correct. It can be tricky getting the contrast and brightness to a good combination for readability. See if your display has a small potentiometer (usually adjustable with a very small Philips-head driver) on the back and carefully adjust the contrast.
Brightness is often changeable through software commands but most LCDs default to high brightness when first booted up.
If changing contrast doesn't work, you may have a real wiring problem and then it really is off-topic for this forum. In that case you should sketch a schematic and post on Electrical Engineering stack.

In Arduino, how do you write to port when port is a variable?

Examples of writing to a port seem to always use the port number as a constant, eg,
OCR2A = 180;
How do you write to the port when the port is unknown until run time. For example,
int port = (buttonPressed) ? 0x3b : 0x3c;
portWrite( port, 180 );
What I cannot find is the funtion portWrite(). Does something like that exist?
Robert's answer has some imprecise assertions and an incomplete answer.
Writing directly to port registers you can ruin other settings of the port and sometimes cause permanent damage to controller.
Can ruin other settings: true, you have to know what you are doing (for instance what pins are on the port you are manipulating, and know what are the functions you want to keep.
Can cause permanent damage: not really, or better not because of the port manipulation. If you wire a short circuit to ground and then set it as an output to 1, you can damage it whether you are using the port register or the digitalwrite. You have to be careful in both ways.
Now, returning to your problem, the enumeration is one way, but since the PORTB, PORTC, PORTD are just short name for values, you can set a variable and then use it to indirectly access it.
The type for this kind of variable is a volatile pointer to a byte (volatile means that write and read operations cannot be optimized by the compiler, since the value can change even between two operations):
volatile uint8_t *variablePortRegister;
You just have to load it with the address (so with the & symbol) of the register you want to change:
variablePortRegister = &PORTC;
then use the pointer to change the value
PORTC = 0x12;
becomes
(*variablePortRegister) = 0x12;
This is a short example. For it to work, connect a LED with resistor on arduino pin 5 (bit 5 of PORTD). The LED on the board (labeled L) is connected to pin 13 (bit 5 of PORTB).
The sketch will make one of the two leds blink for five times, then switch to the other. Only port manipulation instructions are used, and you can find that the way to read the port is the same as the one to write it.
volatile uint8_t *myportreg;
unsigned long lastTime;
uint8_t counter;
void setup() {
DDRB |= 0x20;
DDRD |= 0x20;
PORTB = 0;
PORTD = 0;
counter = 99; // trigger the register change immediately
}
void loop() {
if (counter >= 10)
{
counter = 0;
if (myportreg == &PORTD)
myportreg = &PORTB;
else
myportreg = &PORTD;
}
if ((millis() - lastTime) > 500)
{
lastTime = millis();
// change bit 5 of register
*myportreg = 0x20 ^ (*myportreg);
counter++;
}
}
EDIT: as Robert pointed out, it's much better to "use" just the pins you need (in this case, bit 5 of port B and D) rather than setting the whole port; this way you minimize the risk of screwing up something else. This edit is already included in the above code, so the code is correct
The port is a bit in one particular register. If you know the register and the position of the port in that particular register you can try this:
register = (1<<port) || register
to set the port to 1 and
register = (1<<port)^-1 && register
to set the port to 0.
Of course, you will need a switch somewhere to determine the register and the bit of the port in the register given the port name.

Power monitoring on a three phase system with Arduino Uno

Hello
I am currently working on a project, where I want to measure the voltage and current in a 3-phase system with an Arduino Uno.
This is a small schoolproject and I've had the necessary course on AC-systems to know about safety around higher voltages. I've also have a little bit experience with microcontroller but I've never used ADC.
I have a problem when reading from the analog pins of the Arduino Uno. It seems like the analog pins are mixed which i believe is called ghosting. I've been searching the internet for some answers to this matter, but the proposed solutions didn't work for me. I tried to make a dummy measurement and also to make a small time delay between measurements but since it's about power monitoring timing is critical. I need at minimum 20 readings which needs to be done in 20ms
To test the code I used two function generators. Is this even possible or allowed? Is it best to have at minimum a resistance in between and maybe a capacitor to remove noise?
Is there something in the circuit when transforming the voltage/current to be between 0V-5V there can be done to prevent this ghosting-effect?
I am using a voltagetransformer for the voltage and a Hall-effect sensor for the current. Both circuits need offset.
This is the code that makes the measurements.
void measure(char pin_volt, char pin_curr, int *volt_rms, int *curr_rms, float *theta){
int i;
long squared_v, squared_c, sum_squared_v = 0, sum_squared_c = 0, inst_v, inst_c, mean_squared_v, mean_squared_c;
unsigned long time_v, time_c;
for(i = 0; i < samples; i++){
inst_v = analogRead(pin_volt) - volt_offset;
if(inst_v > -volt_varying && inst_v < volt_varying) {
time_v = micros();
}
inst_c = analogRead(pin_curr) - curr_offset;
if(inst_c >= -curr_varying && inst_c <= curr_varying) {
time_c = micros();
}
squared_v = inst_v * inst_v;
squared_c = inst_c * inst_c;
sum_squared_v += squared_v;
sum_squared_c += squared_c;
delayMicroseconds(80);
}
mean_squared_v = sum_squared_v / samples;
mean_squared_c = sum_squared_c / samples;
*volt_rms = sqrt(mean_squared_v);
*curr_rms = sqrt(mean_squared_c);
*theta = calculate_phase_difference(time_v,time_c);
}
Adding a capacitor can lower the problem.
Try to do the following:
No current or tension on the circuit, so the arduino should measure 0 values.
Run a sketch that reads values and prints max and min values to serial monitor; you will see that values will not be zero as expected, those are interferences.
Try and find a capacitor that can lower those values but don't exagerate.

serial interfacing of 89s52 with Hyperterminal... getting garbage values

I need to transfer data serially from AT89s52 to Hyperterminal of PC.
For that I made a sample program to print "Hello" on the hyperterminal of my PC by programming the below given code inside 89s52 microcontroller and connecting it to my PC via serial port. Now when I am opening hyperterminal for the respective port, I should be seeing "Hello" printed on the screen multiple times, but what actually I am seeing is some garbage data getting printed.
This is the code I have used.
#include < AT89X52.H>
#include < STDLIB.H>
#include < STDIO.H>
unsigned int i;
void main (void)
{
TMOD = 0x20;
SCON = 0x50;
TH1 = 0xFD;
TL1 = 0xFD;
TR1 = 1;
TI = 1;
P1 = 0;
while (1)
{
puts("Hello\r");
P1 ^= 0x01; /* Toggle P1.0 each time we print */
for(i=0;i<25000;i++);
}
}
In the Hyper terminal I am not getting correct output i.e. Hello. Instead I am seeing some Garbage characters..
Can anybody help on this please..?
Can you See P1 is toggling? I would rather send a single character first and observe what is sending by using an oscilloscope. You should see a digital signal corresponding to the ASCII value of the character being split-out from the TX pin of the micro. Also you can check the baud rate (exact value) by using the scope. If you are convinced that the correct value is sent at the right baud rate it is most likely that you got a bad connection or perhaps the baud rate should slightly be changed.

MSP430 not able to handle double

I am trying to program a MSP430 with a simple "FIR filter" program, that looks like the following:
#include "msp430x22x4.h"
#include "legacymsp430.h"
#define FILTER_LENGTH 4
#define TimerA_counter_value 12000 // 12000 counts/s -> 12000 counts ~ 1 Hz
int i;
double x[FILTER_LENGTH+1] = {0,0,0,0,0};
double y = 0;
double b[FILTER_LENGTH+1] = {0.0338, 0.2401, 0.4521, 0.2401, 0.0338};
signed char floor_and_convert(double y);
void setup(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
BCSCTL1 = CALBC1_8MHZ; // Set DCO
DCOCTL = CALDCO_8MHZ;
/* Setup Port 3 */
P3SEL |= BIT4 + BIT5; // P3.4,5 = USART0 TXD/RXD
P3DIR |= BIT4; // P3.4 output direction
/* UART */
UCA0CTL1 = UCSSEL_2; // SMCLK
UCA0BR0 = 0x41; // 9600 baud from 8Mhz
UCA0BR1 = 0x3;
UCA0MCTL = UCBRS_2;
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI_A0 RX interrupt
/* Setup TimerA */
BCSCTL3 |= LFXT1S_2; // LFXT1S_2: Mode 2 for LFXT1 = VLO
// VLO provides a typical frequency of 12kHz
TACCTL0 = CCIE; // TACCR0 Capture/compare interrupt enable
TACCR0 = TimerA_counter_value; // Timer A Capture/Compare 0: -> 25 Hz
TACTL = TASSEL_1; // TASSEL_1: Timer A clock source select: 1 - ACLK
TACTL |= MC_1; // Start Timer_A in up mode
__enable_interrupt();
}
void main(void) // Beginning of program
{
setup(); // Call Function setup (see above)
_BIS_SR(LPM3_bits); // Enter LPM0
}
/* USCIA interrupt service routine */
/*#pragma vector=USCIAB0RX_VECTOR;*/
/*__interrupt void USCI0RX_ISR(void)*/
interrupt (USCIAB0RX_VECTOR) USCI0RX_ISR(void)
{
TACTL |= MC_1; // Start Timer_A in up mode
x[0] = (double)((signed char)UCA0RXBUF); // Read received sample and perform type casts
y = 0;
for(i = 0;i <= FILTER_LENGTH;i++) // Run FIR filter for each received sample
{
y += b[i]*x[i];
}
for(i = FILTER_LENGTH-1;i >= 0;i--) // Roll x array in order to hold old sample inputs
{
x[i+1] = x[i];
}
while (!(IFG2&UCA0TXIFG)); // Wait until USART0 TX buffer is ready?
UCA0TXBUF = (signed char) y;
TACTL |= TACLR; // Clear TimerA (prevent interrupt during receive)
}
/* Timer A interrupt service routine */
/*#pragma vector=TIMERA0_VECTOR;*/
/*__interrupt void TimerA_ISR (void)*/
interrupt (TIMERA0_VECTOR) TimerA_ISR(void)
{
for(i = 0;i <= FILTER_LENGTH;i++) // Clear x array if no data has arrived after 1 sec
{
x[i] = 0;
}
TACTL &= ~MC_1; // Stops TimerA
}
The program interacts with a MatLab code, that sends 200 doubles to the MSP, for processing in the FIR filter. My problem is, that the MSP is not able to deal with the doubles.
I am using the MSPGCC to compile the code. When I send a int to the MSP it will respond be sending a int back again.
Your problem looks like it is in the way that the data is being sent to the MSP.
The communications from MATLAB is, according to your code, a sequence of 4 binary byte values that you then take from the serial port and cast it straight to a double. The value coming in will have a range -128 to +127.
If your source data is any other data size then your program will be broken. If your data source is providing binary "double" data then each value may be 4 or 8 bytes long depending upon its internal data representation. Sending one of these values over the serial port will be interpreted by the MSP as a full set of 4 input samples, resulting in absolute garbage for a set of answers.
The really big question is WHY ON EARTH ARE YOU DOING THIS IN FLOATING POINT - on a 16 bit integer processor that (many versions) have integer multiplier hardware.
As Ian said, You're taking an 8bit value (UCA0RXBUF is only 8 bits wide anyway) and expecting to get a 32bit or 64 bit value out of it.
In order to get a proper sample you would need to read UCA0RXBUF multiple times and then concatenate each 8 bit value into 32/64 bits which you then would cast to a double.
Like Ian I would also question the wisdom of doing floating point math in a Low power embedded microcontroller. This type of task is much better suited to a DSP.
At least you should use fixed point math, seewikipedia (even in a DSP you would use fixed point arithmetic).
Hmm. Actually the code is made of my teacher, I'm just trying to make it work on my Mac, and not in AIR :-)
MATLAB code is like this:
function FilterTest(comport)
Fs = 100; % Sampling Frequency
Ts = 1/Fs; % Sampling Periode
L = 200; % Number of samples
N = 4; % Filter order
Fcut = 5; % Cut-off frequency
B = fir1(N,Fcut/(Fs/2)) % Filter coefficients in length N+1 vector B
t = [0:L-1]*Ts; % time array
A_m = 80; % Amplitude of main component
F_m = 5; % Frequency of main component
P_m = 80; % Phase of main component
y_m = A_m*sin(2*pi*F_m*t - P_m*(pi/180));
A_s = 40; % Amplitude of secondary component
F_s = 40; % Frequency of secondary component
P_s = 20; % Phase of secondary component
y_s = A_s*sin(2*pi*F_s*t - P_s*(pi/180));
y = round(y_m + y_s); % sum of main and secondary components (rounded to integers)
y_filt = round(filter(B,1,y)); % filtered data (rounded to integers)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Serial_port_object = serial(comport); % create Serial port object
set(Serial_port_object,'InputBufferSize',L) % set InputBufferSize to length of data
set(Serial_port_object,'OutputBufferSize',L) % set OutputBufferSize to length of data
fopen(Serial_port_object) % open Com Port
fwrite(Serial_port_object,y,'int8'); % send out data
data = fread(Serial_port_object,L,'int8'); % read back data
fclose(Serial_port_object) % close Com Port
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
subplot(2,1,1)
hold off
plot(t,y)
hold on
plot(t,y_filt,'r')
plot(t,y_filt,'ro')
plot(t,data,'k.')
ylabel('Amplitude')
legend('y','y filt (PC)','y filt (PC)','y filt (muP)')
subplot(2,1,2)
hold off
plot(t,data'-y_filt)
hold on
xlabel('time')
ylabel('muP - PC')
figure(1)
It is also not advised to keep interrupt routines doing long processing routines, because you will impact on interrupt latency. Bytes comming from the PC can get easily lost, because of buffer overrun on the serial port.
The best is to build a FIFO buffer holding a resonable number of input values. The USCI routine fills the FIFO while the main program keeps looking for data inside it and process them as they are available.
This way, while the data is being processed, the USCI can interrupt to handle new incomming bytes.
When the FIFO is empty, you can put the main process in a suitable LPM mode to conserve power (and this is the best MSP430 feature). The USCI routine will wake the CPU up when a data is ready (just put the WAKEUP attribute in the USCI handler if you are using MSPGCC).
In such a scenario be sure to declare volatile every variable that are shared between interrupt routines and the main process.

Resources