我正在尝试使用简单的“FIR滤波器”程序对MSP430进行编程,如下所示:
#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
}
程序与MatLab代码交互,该代码向MSP发送200个双打,以便在FIR滤波器中进行处理。我的问题是,MSP无法处理双打。 我正在使用MSPGCC来编译代码。当我向MSP发送一个int时,它会响应再次发送一个int。
答案 0 :(得分:2)
您的问题看起来像是将数据发送到MSP的方式。
根据您的代码,来自MATLAB的通信是一系列4个二进制字节值,然后您可以从串行端口获取并将其直接转换为double。进入的值将在-128到+127之间。
如果您的源数据是任何其他数据大小,那么您的程序将被破坏。如果您的数据源提供二进制“双”数据,那么每个值可能是4或8个字节长,具体取决于其内部数据表示。通过串口发送其中一个值将被MSP解释为一组完整的4个输入样本,从而导致一组答案的绝对垃圾。
真正重要的问题是你在浮动点上做了什么 - 在一个16位整数处理器上(许多版本)都有整数乘法器硬件。
答案 1 :(得分:1)
正如Ian所说,你正在采用一个8位的值(UCA0RXBUF无论如何只有8位宽),并希望得到一个32位或64位的值。
为了获得正确的样本,您需要多次读取UCA0RXBUF,然后将每个8位值连接成32/64位,然后将其转换为double。
与Ian一样,我也会质疑在低功耗嵌入式微控制器中进行浮点运算的智慧。这类任务更适合DSP。
至少你应该使用定点数学,参见wikipedia(即使在DSP中你也会使用定点运算)。
答案 2 :(得分:0)
嗯。实际上代码是由我的老师制作的,我只是想让它在我的Mac上运行,而不是在AIR中运行: - )
MATLAB代码是这样的:
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)
答案 3 :(得分:0)
也不建议让中断例程执行长处理例程,因为这会影响中断延迟。由于串口缓冲区溢出,PC上的字节很容易丢失。
最好的是构建一个FIFO缓冲区,其中包含可合理数量的输入值。 USCI例程填充FIFO,而主程序一直在查找其中的数据并在它们可用时进行处理。
这样,在处理数据时,USCI可以中断处理新的输入字节。
当FIFO为空时,您可以将主进程置于合适的LPM模式以节省功耗(这是最佳 MSP430功能)。数据就绪时,USCI例程会唤醒CPU(如果你使用MSPGCC,只需将WAKEUP属性放在USCI处理程序中)。
在这种情况下,请务必声明 volatile 在中断例程和主进程之间共享的每个变量。