nfsboy 发表于 2011-7-18 00:07:42

bascom ATMEGA8 PPM解码后舵机总是微弱抖动

大家好!这个PPM解码程序是网上下的,然后经过我的修改直接输出PWM信号驱动舵机。
但是脉宽信号总是会来回跳跃,不知道哪里错了请高手指点迷津。
代码如下:
                                                            '******************************************************
'======================================================
'System settings
'======================================================
'Definition for Mega 8
$regfile "m8def.dat"


'System frequency (8Mhz)
$crystal = 16000000
Config Lcd = 16 * 2
'Config Lcd = 20 * 4
Config Lcdpin = Pin , Db4 = Portb.0 , Db5 = Portb.1 , Db6 = Portb.2 , Db7 = Portb.3 , E = Portb.4 , Rs = Portb.5
Config Lcdmode = Port
Config Servos = 1 , Servo1 = Portc.3 , Reload = 10
'Config Timer0 = Timer , Prescale = 1
Config Portc.3 = Output
'======================================================
'Configurations
'======================================================

'I/O-Ports
'Config Portc = Output
Config Portd.2 = Input

'Size of display
                                       'it is 4x27 really, but the compiler doesn't understand

'Interrup 0
Config Int0 = Falling                                       'timing of pulses on negative flank

'Configuration of timer 1 (16 bit)
Config Timer1 = Timer , Prescale = 8

'======================================================
'Declarations
'======================================================

Dim ___lcde As Byte                                       'for switching between upper and lower LCD half
Dim N_sync As Byte
Dim Rc_value As Word
Dim Channel(7) As Word                                    '9 channels plus the synchonisation pulse
Dim N_ch As Word
Dim Ch_time(6) As Word
Dim Failsafe_value(6) As Word                               'These will be used when no signal is available
Dim I As Integer                                          'Running parameter for loops
Dim Failsafemode As Bit
Dim Signal_up_flag As Bit
Dim Signal_valid_flag As Bit
Dim X As Integer , X1 As String * 3 , Xstr As String * 10
Dim A As Integer , B As Integer , Ax As Integer , Bx As Integer
Dim Ml As Integer , Mr As Integer , Mm As Integer , X2 As Integer       '

'======================================================
'Initialisations
'======================================================

'Interrupt-Service-Routines
On Timer1 Signal_down                                       'overflow of timer occurs when the signal is down
On Int0 Read_signal                                       'On int0 timing of the signal pulses

'Timer-release
Enable Timer1

'Release of interrupts
Enable Int0
Enable Interrupts

'Initialise ports
'Portc = &B00000000                                          'Note: my hardware has non-inverted LED ports: 1 = on, 0 = off

'Initialise variables
Signal_up_flag = 1                                          'Signal available
Signal_valid_flag = 0                                       'Signal not valid
N_ch = 1
N_sync = 0                                                'Start at channel 1
                                                            'No sybcronisation pulse seen yet

'Failsafe settings
Failsafemode = 0                                          '0 for "keep old signals", 1 for the failsafe values below
Failsafe_value(1) = 1600                                    'Set failsafe value ofchannel 1 (roll)
Failsafe_value(2) = 1600                                    'Set failsafe value ofchannel 2 (pitch)
Failsafe_value(3) = 1050                                    'Set failsafe value ofchannel 3 (yaw)
Failsafe_value(4) = 1600                                    'Set failsafe value ofchannel 4 (power)
Failsafe_value(5) = 1600
Failsafe_value(6) = 1600


'Initialise timer channel values
For I = 1 To 6
   Ch_time(i) = Failsafe_value(i)
Next

'Calibration of the internal oscillator.
'Write here the hex value of calibration byte 3 from the locks and fuses tab
Osccal = &HAF                                             'Loading the calibration byte in the OSCCAL register


'======================================================
'Writing LCD headers
'======================================================

Cls                                                         'Clear lines

Cursor Off

'======================================================
'Main program
'======================================================
A = 2
B = -2
Ml = 60
Mr = 70
Do

   X = Ch_time(1)
'X = X - 63
'   X1 = X1 + X
   Xstr = Str(x)
   X1 = Left(xstr , 3)
'Next I
'X1 = X1 / 10
   X = Val(x1)
   X = X - 175
   X2 = X - Mm



If X < Ml Then
    Ml = X
Elseif X > Mr Then
    Mr = X
End If

Mm = Ml + Mr
Mm = Mm / 2

'Calculate timing values in micro sec, only if the signal is up (simple fail safe implementation)
'Otherwise leave old values or use faisafe values
'The ch_time() array would be used for further processing
'The channel() array is only used for measurement and may contain invalid data
   If Signal_valid_flag = 1 Then
      For I = 1 To 6
         Ch_time(i) = Channel(i)
      Next
   Else
      If Failsafemode = 1 Then
         For I = 1 To 6
            Ch_time(i) = Failsafe_value(i)
         Next
         End If
       End If
    'Waitms 100

'Writing timing values to LCD display
Cls
   Locate 1 , 1
   Lcd X ; " c1"
   Locate 1 , 9
   Lcd Mm ; " mm"
   Locate 2 , 1
   Lcd Ml ; " ml"
    Locate 2 , 9
   Lcd Mr ; " mr"
   If X2 > A Or X2 < B Then

   Servo(1) = Mm + X2
End If

   Portc.0 = Signal_valid_flag                              'Showing the signal valid flag on port C0
   Portc.1 = Signal_up_flag                                 'Showing the signal up flag on port C1

'waitstate to get readable LCD values
' For I = 1 To 10

Loop

End

'******************************************************
'Interrupt Service Routines
'******************************************************
Read_signal:
   Rc_value = Timer1                                        'First thing: store the timer value in an intermediate variable.
   Timer1 = 0                                             'Then reset the timer

   If Rc_value > 6000 Then                                  'If the puls is to long it is the synchronisation pulse,
      N_ch = 6                                              'so set channel number to number of synchonisation pulse
      If N_sync < 2 Then Incr N_sync                        'The sync pulse counter is increased until it is 2
      If N_sync = 2 Then Signal_valid_flag = 1            'If it is the second consequitive sync puls, then the signal is declared valid
   End If
   Channel(n_ch) = Rc_value                                 'Load the measured timer value into the array of measurement data
   Incr N_ch                                                'next channel
   If N_ch = 7 Then N_ch = 1                              'Limit the number of channels to 10
   Signal_up_flag = 1                                       'set the signal up flag
Return


Signal_down:                                                'if no signal is detected for 65 ms
   Signal_up_flag = 0                                       'reset signal_up_flag
   Signal_valid_flag = 0                                    'reset signal valid flag
   N_sync = 0                                             'reset number of syncpulses passed
Return

catluoq 发表于 2011-9-15 23:13:52

程序用了int0中断,但中断处理过程不够简练,里面用了很影响速度的WORD型变量,还有那么多条件语句,速度跟不上,有效脉宽(可变部分)本来就只有1000us,中断如果用了50us,输出自然受干扰比较厉害。
    据我的经验,最好还是调用BASCOM里的 pulsein 功能,即用轮询方式测量脉宽,当然轮询方式解PPM码的缺点是,当所有脉宽都测完后,距离下一个信号周期已经比较近了,可能用pulseout功能向舵机输出信号的时候比较局促,必须采用同时向4-5个舵机同时开始发高电平,然后各自在正确的时间降为低电平,当时我用的是很可怜的TINY13,内部RC主频9.6M,解出4个通道,稳稳的不会抖。
    如果一定要用中断,最好在中断里全部使用汇编,可参考以下我在电调里使用的方式,但要注意,程序里使用的是TCNT0,最高约60级精度,一般情况下足够用了:

config timer0=timer , prescale = 256

isr_int0:
'For mega8

in r2, sreg          '保存 SREG,在我的程序里其它地方没使用r2及r18,故不用PUSH,POP,节省了若干时钟周期
in r18,mcucr                                                'get int0 state
in r0,tcnt0                                                 'get tcnt0
incr18                                                    'goggle r16.0
andi r18,1                                                'erash r16.1-7
sts {newrcin},r18                                           'newrcin 为一 BYTE 值,1或0,表明是否收到新的脉冲信号
mul r0,r18                                                '硬件乘法器
sts {pwmctlppm} ,r0                                       'PWMCTLPPM 为有效脉冲长度值,BYTE 类
ori r18,2
!out mcucr,r18                                              '改变 MCUCR.ISC00 值,即改变下次脉冲触发条件
clr r18                                                   
!out tcnt0,r18                                              'tcnt0 =0
wdr                                                         '喂狗,也可以不用
!out sreg , r2                                              '恢复 SREG

return

    另外,在BASCOM里,用 bit 类值运算,速度很慢,建议全部改为 BYTE 类,可提高效率。

highnose 发表于 2011-10-1 17:04:48

嗯,顶一下,学习ing

初见 发表于 2015-10-9 20:22:41

看不懂,但还是要谢谢

rundream 发表于 2015-11-2 15:02:31

看到汇编直接晕菜。前几天我的车也在弄舵机,不抖,但鸣叫。
页: [1]
查看完整版本: bascom ATMEGA8 PPM解码后舵机总是微弱抖动