通过mem32函数来提高MM32 MicroPython 输出PWM 频率的精度
简 介: 利用mem32功能,对于MM32的MicroPython中的PWM频率精度低的问题,通过重新调整ARR来提高PWM频率精度。
关键词: PWM,MM32,MicroPython,mem32
 
  §01 MM32 PWM输出 
  
1.1 版本的优缺点
在MindMotion MM32 单片机上的MicroPython移植-PWM中给出了灵动公司SuYong移植在MM32F3277上的MicroPython版本中具有了PWM。相比于MicroPython官网给出的版本,这个版本的PWM优点:
- 它是基于两个Timer(TIM3、TIM4)给出了八个通道的PWM输出,所以它允许存在两组不同基频的PWM波形输出。从而可以分别控制舵机、直流电机控制等。
但在实现过程中,它的缺点也很明显。SuYong在实现过程中,对于ARR固定位999,对应PWM的CNT范围是从0 ~ 999。可以实现1/1000的输出PWM精度。但这带来对应的输出PWM频率精度受限。
这一点在 使用MM32 MicroPython产生双音频信号的局限性 进行了测量。下图给出从500到2000Hz之内, MM32 PWM频率出现的绝对误差。

▲ 图1.1.1 输出频率在500 ~ 2000 Hz之间对应的实际PWM频率误差 
1.2 误差分析
1.2.1 PWM频率确定
假设MM32F3277的Time工作频率来自于MCU的主频,对应 f o s c = 120 M H z f_{osc} = 120MHz fosc=120MHz 。对于 A R R = 999 ARR = 999 ARR=999 ,确定PWM频率是由TIM3/TIM4的预分配器 PSC 来决定。
f P W M = f o s c ( 1 + P S C ) ⋅ ( A R R + 1 ) f_{PWM} = {{f_{osc} } \over {\left( {1 + PSC} \right) \cdot \left( {ARR + 1} \right)}} fPWM=(1+PSC)⋅(ARR+1)fosc
由于PSC必须采用整数,所以对应输出的 f P W M f_{PWM} fPWM 会存在一定的误差。
1.2.2 误差
下面是从500 到1000 范围内设定的频率与实际频率之间的误差。
from headm import *
fset = range(500, 2000, 5)
def deltaf(f):
    fosc = 120e6
    psc = int(fosc/f/1000)-1
    freal = fosc/(1+psc)/1000
    return f-freal
fdel = [deltaf(f) for f in fset]
plt.plot(fset, fdel)
plt.xlabel("Set Frequency(Hz)")
plt.ylabel("Delta Frequency(Hz)")
plt.grid(True)
plt.tight_layout()
plt.show()
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20

▲ 从500 - 1000 内对应的频率误差 
  下面是每隔5Hz计算出来的PWM 频率误差,这与在使用MM32 MicroPython产生双音频信号的局限性中实际测量的结果基本上很接近了。
 
▲ 图1.2.2 500 ~ 2000Hz 之间每隔5Hz计算出的理论频率误差 
1.3 如何改进PWM频率误差?
这个问题主要来自于在SuYong的PWM功能实现过程中,固定ARR为999,这就使得实际输出的PWM频率无法进行小的调整。下面给出一个计算方式,使得ARR在999范围前后,能够根据误差进行调整,使得输出的频率满足设定的频率。
1.3.1 修改方式
具体修改方式分为两步,有下面的 函数给出:
def deltaf(f):
    fosc = 120e6
    psc = int(fosc/f/1000)-1
    arr = int(fosc/(1+psc)/f) - 1
    freal = fosc/(1+psc)/(1+arr)
    return f-freal,arr
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
1.3.2 修改结果
  利用这种方式修改之后,对应的ARR在1000左右进行浮动,可以看到对应的频率误差都小于1/1000
 
▲ 对应的频率误差与ARR取值 
1.4 在MicroPython实现
根据MM32F3277 MicroPython的 mem 函数对于MCU内存访问描述,可以利用mem32技术直接访问MM32F3277的寄存器,所以可以将上面修正后的频率所需要对应的PSC, ARR利用Mem32进行调整。从而实现提高PWM 输出频率的精度。
1.4.1 实现代码
#------------------------------------------------------------
from micropython            import const
APB1PERIPH_BASE = const(0x40000000)
TIM3_BASE       = const(APB1PERIPH_BASE + 0x0400)
TIM4_BASE       = const(APB1PERIPH_BASE + 0x0800)
TIM_TYPE_CR1    = const(0*4)
TIM_TYPE_CR2    = const(1*4)
TIM_TYPE_SR     = const(4*4)
TIM_TYPE_CNT    = const(9*4)
TIM_TYPE_PSC    = const(10*4)
TIM_TYPE_ARR    = const(11*4)
TIM_TYPE_CCR1   = const(13*4)
TIM_TYPE_CCR2   = const(14*4)
TIM_TYPE_CCR3   = const(15*4)
TIM_TYPE_CCR4   = const(16*4)
def pwmFreq(f, pwm):
    fosc = 96e6
    psc = int(fosc/f/1000) - 1
    arr = int(fosc/(1+psc)/f) - 1
    if pwm < 4: base = TIM3_BASE
    else: base = TIM4_BASE
    mem32[base+TIM_TYPE_PSC] = psc
    mem32[base+TIM_TYPE_ARR] = arr
    return arr
#------------------------------------------------------------
pwmFreq(697, 0)
pwmFreq(697, 4)
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
1.4.2 测试结果
实际测量对应的输出频率结果为 697.32Hz。
  测试设定PWM频率与实际输出频率之间的误差。为什么是这样,现在无法解释。
 
▲ 图1.4.1 500 到 2000Hz 之间的设置与输出频率之间的误差 
下面对于arr的取值进行调整,进行四舍五入。 另外对于ARR设置为10000左右,可以看到误差频率在10000分之一左右。

▲ 图1.4.2 频率在500-2000Hz之间的设置误差 

▲ 图1.4.3 在频率 500 - 1600Hz之内的频率误差 
 
  ※ 总  结 ※ 
  
利用mem32功能,对于MM32的MicroPython中的PWM频率精度低的问题,通过重新调整ARR来提高PWM频率精度。
2.1 软件代码
2.1.1 MicroPython程序
from machine                import Pin,mem32,PWM
import utime
led = Pin('PB2', Pin.OUT_PUSHPULL)
f = 697
pwm0 = PWM(0, freq=f, duty=500)
pwm1 = PWM(4, freq=f, duty=500)
from micropython            import const
APB2PERIPH_BASE = const(0x40010000)
UART1_BASE    = const(APB2PERIPH_BASE + 0x3800)
UART1_RDR     = const(UART1_BASE + 1*4)
UART1_CSR     = const(UART1_BASE + 2*4)
REPLBUF_LENGTH = const(64)
replbuf = [0]*REPLBUF_LENGTH
replpoint = 0
def procREPL(f):
    global replbuf,replpoint
    if mem32[UART1_CSR] & 0x2:
        bc = mem32[UART1_RDR]
        if replpoint < REPLBUF_LENGTH-1:
            replbuf[replpoint] = bc
            replpoint += 1
        if bc == 13:
            f(bytes(replbuf[0:replpoint-1]))
            replpoint = 0
from micropython            import const
APB1PERIPH_BASE = const(0x40000000)
TIM3_BASE       = const(APB1PERIPH_BASE + 0x0400)
TIM4_BASE       = const(APB1PERIPH_BASE + 0x0800)
TIM_TYPE_CR1    = const(0*4)
TIM_TYPE_CR2    = const(1*4)
TIM_TYPE_SR     = const(4*4)
TIM_TYPE_CNT    = const(9*4)
TIM_TYPE_PSC    = const(10*4)
TIM_TYPE_ARR    = const(11*4)
TIM_TYPE_CCR1   = const(13*4)
TIM_TYPE_CCR2   = const(14*4)
TIM_TYPE_CCR3   = const(15*4)
TIM_TYPE_CCR4   = const(16*4)
def pwmFreq(f, pwm, duty):
    fosc = 96e6
    psc = int(fosc/f/10000) - 1
    arr = int(fosc/(1+psc)/f+0.5)
    if pwm < 4: base = TIM3_BASE
    else:
        base = TIM4_BASE
        pwm -= 4
    mem32[base+TIM_TYPE_PSC] = psc
    mem32[base+TIM_TYPE_ARR] = arr
    ccr = int(arr*duty)
    mem32[base+TIM_TYPE_CCR1+pwm*4]
    return arr
def f(s):
    global pwm0,pwm1
    frq= int(s)
    print(frq)
    pwmFreq(frq, 0, 0.5)
    pwmFreq(frq, 4, 0.5)
while True:
    procREPL(f)
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
2.1.2 测试代码
#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# TEST3.PY                     -- by Dr. ZhuoQing 2022-02-05
#
# Note:
#============================================================
from headm import *                 # =
from tsmodule.tsstm32       import *
setf = range(500, 2000, 10)
outf = []
stm32cmd('SNDCD%d\r'%setf[0])
time.sleep(2)
for f in setf:
    stm32cmd('SNDCD%d\r'%f)
    time.sleep(2)
    meter = meterval()
    outf.append(meter[0])
    printff(f, meter[0])
    tspsave('measure', setf=setf, outf=outf)
delf = [f1-f2 for f1,f2 in zip(setf, outf)]
plt.plot(setf, delf)
plt.xlabel("SetFrequency")
plt.ylabel("Delta Frequency")
plt.grid(True)
plt.tight_layout()
plt.show()
printf('\a')
#------------------------------------------------------------
#        END OF FILE : TEST3.PY
#============================================================
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
2.1.3 mm32代码
from headm import *                 # =
caretpos = list(tspgetcaretpos())
headspaceself = '\r\n' +  ' '*caretpos[0] + 'self.'
headspace = '\r\n' +  ' '*caretpos[0]
headspaceonly = ' '*caretpos[0]
returnpos = 0
insertstr = ''
if len(sys.argv) > 1:
    if sys.argv[1] == 'repl':
        codestr = (
"from micropython   import const,mem32",
"APB2PERIPH_BASE = const(0x40010000)",
"UART1_BASE    = const(APB2PERIPH_BASE + 0x3800)",
"UART1_RDR     = const(UART1_BASE + 1*4)",
"UART1_CSR     = const(UART1_BASE + 2*4)",
"REPLBUF_LENGTH = const(64)",
"replbuf = [0]*REPLBUF_LENGTH",
"replpoint = 0",
"def procREPL(f):",
"    global replbuf,replpoint",
"    if mem32[UART1_CSR] & 0x2:",
"        bc = mem32[UART1_RDR]",
"        if replpoint < REPLBUF_LENGTH-1:",
"            replbuf[replpoint] = bc",
"            replpoint += 1",
"        if bc == 13:",
"            f(bytes(replbuf[0:replpoint-1]))",
"            replpoint = 0",
"def f(s):",
"    print(int(s))\r\n",
        )
        insertstr = headspace.join(codestr).rstrip(' ')
    elif sys.argv[1] == 'pwmf':
        codestr = (
"from micropython            import const",
"APB1PERIPH_BASE = const(0x40000000)",
"TIM3_BASE       = const(APB1PERIPH_BASE + 0x0400)",
"TIM4_BASE       = const(APB1PERIPH_BASE + 0x0800)",
"TIM_TYPE_CR1    = const(0*4)",
"TIM_TYPE_CR2    = const(1*4)",
"TIM_TYPE_SR     = const(4*4)",
"TIM_TYPE_CNT    = const(9*4)",
"TIM_TYPE_PSC    = const(10*4)",
"TIM_TYPE_ARR    = const(11*4)",
"TIM_TYPE_CCR1   = const(13*4)",
"TIM_TYPE_CCR2   = const(14*4)",
"TIM_TYPE_CCR3   = const(15*4)",
"TIM_TYPE_CCR4   = const(16*4)",
"def pwmFreq(f, pwm, duty):",
"    fosc = 96e6",
"    psc = int(fosc/f/10000) - 1",
"    arr = int(fosc/(1+psc)/f+0.5)",
"    if pwm < 4: base = TIM3_BASE",
"    else:",
"        base = TIM4_BASE",
"        pwm -= 4",
"    mem32[base+TIM_TYPE_PSC] = psc",
"    mem32[base+TIM_TYPE_ARR] = arr",
"    ccr = int(arr*duty)",
"    mem32[base+TIM_TYPE_CCR1+pwm*4]",
"    return arr\r\n",
        )
        insertstr = headspace.join(codestr).rstrip(' ')
    elif sys.argv[1] == 'xxxx':
        codestr = (
        )
        insertstr = headspace.join(codestr).rstrip(' ')
    else:
        printf("Unrecoginized argument.\a")
        exit()
if len(insertstr) > 0:
    clipboard.copy(insertstr)
    tsppasteclipboard()
    if returnpos > 0:
        tspsetcaretpos(caretpos[0], caretpos[1])
printf('\a')
  
 - 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
■ 相关文献链接:
- MindMotion MM32 单片机上的MicroPython移植-PWM
- 使用MM32 MicroPython产生双音频信号的局限性
- MM32F3277 MicroPython的 mem 函数对于MCU内存访问
● 相关图表链接:
- 图1.1.1 输出频率在500 ~ 2000 Hz之间对应的实际PWM频率误差
- 从500 - 1000 内对应的频率误差
- 图1.2.2 500 ~ 2000Hz 之间每隔5Hz计算出的理论频率误差
- 对应的频率误差与ARR取值
- 图1.4.1 500 到 2000Hz 之间的设置与输出频率之间的误差
- 图1.4.2 频率在500-2000Hz之间的设置误差
- 图1.4.3 在频率 500 - 1600Hz之内的频率误差
文章来源: zhuoqing.blog.csdn.net,作者:卓晴,版权归原作者所有,如需转载,请联系作者。
原文链接:zhuoqing.blog.csdn.net/article/details/122792626
- 点赞
- 收藏
- 关注作者
 
             
           
评论(0)