小弟最近刚刚开始研究SPWM,在网上找了一个程序,通过dspic30f4011芯片产生SPWM波形,进而控制电机。我想验证spwm波形正确与否,就把它跟一个1K的电阻和104的电容相连,然后用数字示波器看电容两端的波形,结果出来的是三角波,恳请各位大虾帮帮忙~
附上我的程序:
.equ __30F4011, 1
.include "F:\mplab\MPLAB C30\support\inc\p30f4011.inc"
.global __reset
;..............................................................................
; 配置位:
;..............................................................................
config __FOSC, CSW_FSCM_OFF & XT_PLL4 ; 关闭时钟切换和
; 故障保护时钟监视并
; 使用XT 振荡器和4 倍频PLL 作为
; 系统时钟
config __FWDT, WDT_OFF ; 关闭看门狗定时器
config __FBORPOR, PBOR_ON & BORV_27 & PWRT_16 & MCLR_EN
; 设置欠压复位电压
; 并将上电延迟定时器设置为16ms
config __FGS, CODE_PROT_OFF ; 对一般代码段
; 将代码保护设置为关闭
;..............................................................................
; 近数据存储器(RAM 的低8KB)中的未初始化变量
;..............................................................................
.section .nbss, "b"
; 在每个PWM 周期将此变量加到16 位的
; 正弦波数值表指针。对于16 KHz 的PWM 调制,数值246 将提供
;60 Hz 的调制频率
Frequency:.space 2
; 此变量用来设置调制幅值并换算
; 从正弦波数值表获取的值。有效值的范围为0 到32767
Amplitude:.space 2
; 此变量是正弦波数值表的指针。在每次PWM 中断时,
; 它递增一次,递增数值是Frequency 变量的值。
Phase: .space 2
;..............................................................................
; 存储在程序空间中的常数
;..............................................................................
.section .sine_table, "x"
.align 256
; 这是一个含有64 个值的正弦波表,覆盖了正弦函数的360 度。
; 使用Microsoft Excel 计算这些值并将它们粘贴到此程序中。
SineTable:
.hword 0,3212,6393,9512,12539,15446,18204,20787,23170,25329
.hword 27245,28898,30273,31356,32137,32609,32767,32609,32137,31356,30273,28898
.hword 27245,25329,23170,20787,18204,15446,12539,9512,6393,3212,0,-3212,-6393
.hword -9512,-12539,-15446,-18204,-20787,-23170,-25329,-27245,-28898,-30273
.hword -31356,-32137,-32609,-32767,-32609,-32137,-31356,-30273,-28898,-27245
.hword -25329,-23170,-20787,-18204,-15446,-12539,-9512,-6393,-3212
;..............................................................................
; 此应用程序中的常数
;..............................................................................
; 此常量用来将正弦查找值换算到PWM 占空比的有效范围内。占空比的范围取决于写入PTPER 的值。
; 对于此应用程序,我们将设置PTPER = 230,使占空比介于0 和460 之间。正弦表数据是有符号的,
; 我们用230 乘以表中数据,然后将乘积加上固定的偏移值从而将查找数据换算为正值
.equ PWM_Scaling, 230
; 正弦波表的指针是16 位的。把0x5555 加给
; 指针将提供120 度的偏移,而加上0xAAAA 将提供240
; 度的偏移。这些偏移用来获取PWM 输出的相位2 和相位3 的查找值。
.equ Offset_120, 0x5555
;..............................................................................
; 程序存储器中的代码部分
;..............................................................................
.text ;代码部分的开始
__reset:
MOV #__SP_init, W15 ; 初始化堆栈指针
MOV #__SPLIM_init, W0 ; 初始化堆栈指针限制寄存器
MOV W0, SPLIM
NOP ; 在初始化SPLIM 之后,加一条NOP
CALL _wreg_init ; 调用 _wreg_init 子程序
; 可以选用RCALL 代替CALL
call Setup ; 调用程序以设置I/O 和PWM
;------------------------------------------------------------------------------
; 初始化变量
;------------------------------------------------------------------------------
clr Frequency
clr Amplitude
;------------------------------------------------------------------------------
; 主循环代码
; 在主循环中查询PWM 中断标志
;------------------------------------------------------------------------------
Loop: btss IFS2,#PWMIF ; 查询PWM 中断标志
bra CheckADC ; 如果置1,则继续
call Modulation ; 调用正弦波调制程序
bclr IFS2, #PWMIF ; 清零PWM 中断标志
CheckADC:
btss IFS0,#ADIF
bra Loop
call ReadADC
bra Loop
;------------------------------------------------------------------------------
; ADC 处理子程序
;------------------------------------------------------------------------------
ReadADC:
push.d W0
push.d W4
mov ADCBUF0,W0 ; 将ADC 结果读入W0
mov ADCBUF1,W1 ; 和W1。
asr W0,#2,W4 ; 右移2 位以得到
mov W4,Frequency ; 调制频率。
sl W1,#5,W4 ; 将AN7 和AN12 的值左移以得到
sl W0,#5,W5 ; 1.15 格式的小数数据。
mpy W4*W5,A ; 将频率与V/Hz 的商相乘得到
sac A,W0 ; 调制幅值。将结果存储在W0 中。
mov #28000,W1 ; 限制调制幅值以避免
cp W1,W0 ; 在PWM 调制中由死区引起的
bra GE,NoLimit ; 失真。
mov W1,W0
NoLimit:
mov W0,Amplitude
pop.d W4
pop.d W0
return
;------------------------------------------------------------------------------
; PWM 正弦波调制子程序
;------------------------------------------------------------------------------
Modulation:
push.d W0 ; 保存工作寄存器
push.d W2
push.d W4
push.d W6
push.d W8
push.d W10
; 下面的三条指令初始化TBLPAG 和指针寄存器
; 从而可使用读表操作来访问程序存储器中的正弦波数据。
mov #tblpage(SineTable),W0
mov W0,TBLPAG
mov #tbloffset(SineTable),W0
; 下面的指令块装载正弦波调制程序中使用的各种常数和变量。
mov Phase,W1 ; 装载正弦波表指针
mov #Offset_120,W4 ; 这是120 度偏移的值
mov Amplitude,W6 ; 装载幅值换算系数
mov #PWM_Scaling,W7 ; 装载PWM 换算值
mov Frequency,W8 ; 装载将在每次中断时与表指针相加的Frequency 常数。
; 这是指针调节代码。将Frequency 值加给正弦表指针以使指针在正弦表中前移。
; 然后,为此指针加上相应的偏移值以得到相位2 和相位3 的指针。
; 注意:如果需要不同的相位偏移,可以在这里使用其他常数值。
; 加上0x4000 将得到90 度偏移,加上0x8000 将
; 提供180 度偏移。 在此将0x5555 装入W4 以提供120 度偏移。
add W8,W1,W1 ; 将Frequency 值加给正弦指针
add W1,W4,W2 ; 加上120 度偏移值以得到相位2 的指针
add W2,W4,W3 ; 再加上120 度偏移以得到相位3 的指针
; 该正弦表有64 个值,所以将指针右移
; 以得到一个6 位的指针值。
lsr W1,#10,W9 ; 将相位1 的指针右移以得到高6 位
sl W9,#1,W9 ; 左移一位转换为字节地址
lsr W2,#10,W10 ; 将相位2 的指针右移以得到高6 位
sl W10,#1,W10 ; 左移一位转换为字节地址
lsr W3,#10,W11 ; 将相位3 的指针右移以得到高6 位
sl W11,#1,W11 ; 左移一位转换为字节地址
; 现在,将每个相位的指针与基表指针相加以获得查找值的绝对表地址。
; 然后将查找值换算为正确的幅值和在有效的占空比范围之内。
; 下面的指令块为相位1 计算占空比。为相位2 和相位3 计算占空比的代码与此相同。
add W0,W9,W9 ; 形成相位1 的表地址
tblrdl [W9],W5 ; 读相位1 的查找值
mpy W5*W6,A ; 乘以幅值换算系数
sac A,W5 ; 存储经过换算的结果
mpy W5*W7,A ; 乘以PWM 换算系数
sac A,W8 ; 存储经过换算的结果
add W7,W8,W8 ; 加上PWM 换算系数以产生50% 的偏移
mov W8,PDC1 ; 写PWM 占空比
; 下面的代码块为相位2 计算占空比。
add W0,W10,W10 ; 形成相位2 的表地址
tblrdl [W10],W5 ; 读相位2 的查找值
mpy W5*W6,A ; 乘以幅值换算系数
sac A,W5 ; 存储经过换算的结果
mpy W5*W7,A ; 乘以PWM 换算系数
sac A,W8 ; 存储经过换算的结果
add W7,W8,W8 ; 加上PWM 换算系数以产生50% 的偏移
mov W8,PDC2 ; 写PWM 占空比
; 下面的代码块为相位3 计算占空比。
add W0,W11,W11 ; 形成相位3 的表地址
tblrdl [W11],W5 ; 读相位3 的查找值
mpy W5*W6,A ; 乘以幅值换算系数
sac A,W5 ; 存储经过换算的结果
mpy W5*W7,A ; 乘以PWM 换算系数
sac A,W8 ; 存储经过换算的结果
add W7,W8,W8 ; 加上PWM 换算系数以产生50% 的偏移
mov W8,PDC3 ; 写PWM 占空比
; 现在,保存经过调节的正弦波表指针从而能
; 在此代码的下一次迭代中使用它。
mov W1,Phase
pop.d W10 ; 恢复工作寄存器
pop.d W8
pop.d W6
pop.d W4
pop.d W2
pop.d W0
return ; 从子程序返回
;------------------------------------------------------------------------------
; PWM 和ADC 的设置代码
;-------------------------------------------------------------------------------------
Setup:
; 在使能PWM 之前需要做的第一件事是配置I/O 并复位电源模块。
; 该控制板有一个缓冲PWM 控制线的驱动器IC。
; RD11 端口上的有效低电平使能该缓冲器。
; 此电源模块有一条与端口RE9 相连的高电平有效复位线路。
;clr PORTD
clr PORTE
;mov #0xF7FF,W0 ; 设置RD11 输出驱动PWM 缓冲器
;mov W0,TRISD ; 使能。
mov #0xFFC0,W0;
mov W0,TRISE ; 设置RE0~RE5为输出
; 现在,通过驱动复位线并保持几个微秒来确保电源模块复位。
/*bset PORTE,#9
repeat #39
nop
bclr PORTE,#9 */
; 设置ADC
mov #0x0404,W0 ; 扫描输入
mov W0,ADCON2 ; 每次中断进行2 次采样/ 转换
mov #0x0003,W0;
mov W0,ADCON3 ; Tad 是2 个Tcy
clr ADCHS ;
clr ADPCFG ; 将所有A/D 引脚设置为模拟模式
clr ADCSSL ;
bset ADCSSL,#7 ; 使能对AN7 的扫描
bset ADCSSL,#8 ; 使能对AN8 的扫描
mov #0x8066,W0 ; 使能A/D、PWM 触发和自动采样
mov W0,ADCON1 ;
bclr IFS0,#ADIF ; 清零A/D 中断标志位
; 现在设置PWM 寄存器
mov #0x0077,W0 ; 互补模式,使能#1、#2 和#3
mov W0,PWMCON1 ; 三对PWM 输出
mov #0x000F,W0 ; 器件运行速度为7.38 MIPS 时,将产生2μs 的死区
mov W0,DTCON1
mov #PWM_Scaling, W0 ; 器件运行速度为7.38 MIPS 时,为16KHz PWM 设置周期
mov W0,PTPER
mov #0x0001,W0 ;
mov W0,SEVTCMP ; 将ADC 设置为以特殊事件触发启动
mov #0x0F00,W0 ; 将特殊事件后分频比设置为1:16
mov W0,PWMCON2 ;
mov #0x8002,W0 ; 使能PWM 时基,中心对齐模式
mov W0,PTCON
return ; 从Setup 子程序返回
;..............................................................................
; 子程序:将W 寄存器初始化为0x0000
;..............................................................................
_wreg_init:
CLR W0
MOV W0, W14
REPEAT #12
MOV W0, [++W14]
CLR W14
RETURN
;-------- 所有代码部分结束---------------------------------------------
.end ; 此文件中程序代码的结尾