开源BLHELI-S 代码详细解读(一)

什么是电调固件和电调通讯协议-BLheli电调与Dshot通讯学习笔记 DShot(Digital Shot,是数字协议)(相对于oneshot,oneshot125,oneshot42,multishot等模拟协议)。它最初是由Felix在KISS提出的,后来被Betaflight和BLHeli_S开发团队采用。是用来和刷有BLheli固件的电调通讯的协。BLHeli:最古老的两种开源电调固件之一,由于其广泛的功能和友好的用户界面而变得流行。... 阅读详情
AI摘要

本文对使用51汇编写的BLHELI代码进行详细解读。先介绍代码组成,接着按主流程逐行分析,包括初始化操作、时钟选择、信号检测、油门校准等环节,还提及arming序列及相关检查,最后表示准备阶段解读完毕,下一篇将继续解读驱动电机工作部分。

代码是使用51汇编写的,读之前,先打开汇编指令文档和EFM8BB2 Reference Manual这两个文档备查。 这个有点干涩,也没有其它人做这样的详细解读,一起一行一行读一下吧。

BLHELI主要是由 一堆.inc文件和 BLHeli_S.asm两个组成。 *.inc主要是定义针脚的布局,然后如何开关各个fet等,这个和主逻辑关系不大,我们先不解读*.inc. 

BLHeli_S.asm 是从pgm_start 开始的, 我们按正常的流程一行一行来读,先走主的流程。


;**** **** **** **** **** **** **** **** **** **** **** **** ****
;**** **** **** **** **** **** **** **** **** **** **** **** ****
;**** **** **** **** **** **** **** **** **** **** **** **** ****
;
; Main program start
;
;**** **** **** **** **** **** **** **** **** **** **** **** ****
;**** **** **** **** **** **** **** **** **** **** **** **** ****
;**** **** **** **** **** **** **** **** **** **** **** **** ****

pgm_start:
	; Initialize flash keys to invalid values
	mov	Flash_Key_1, #0
	mov	Flash_Key_2, #0
	; Disable the WDT.
	mov	WDTCN, #0DEh		; Disable watchdog
	mov	WDTCN, #0ADh		
	; Initialize stack
	mov	SP, #0c0h			; Stack = 64 upper bytes of RAM
	; Initialize VDD monitor
IF MCU_48MHZ < 2
	orl	VDM0CN, #080h    	; Enable the VDD monitor
ENDIF
	mov 	RSTSRC, #06h   	; Set missing clock and VDD monitor as a reset source if not 1S capable
	; Set clock frequency
	mov	CLKSEL, #00h		; Set clock divider to 1
	; Switch power off
	call	switch_power_off
	; Ports initialization
	mov	P0, #P0_INIT
	mov	P0MDIN, #P0_DIGITAL
	mov	P0MDOUT, #P0_PUSHPULL
	mov	P0, #P0_INIT
	mov	P0SKIP, #P0_SKIP				
	mov	P1, #P1_INIT
	mov	P1MDIN, #P1_DIGITAL
	mov	P1MDOUT, #P1_PUSHPULL
	mov	P1, #P1_INIT
	mov	P1SKIP, #P1_SKIP				
	mov	P2MDOUT, #P2_PUSHPULL				
	; Initialize the XBAR and related functionality
	Initialize_Xbar	
	; Switch power off again, after initializing ports
	call	switch_power_off
	; Clear RAM
	clr	A				; Clear accumulator
	mov	Temp1, A			; Clear Temp1
	clear_ram:	
	mov	@Temp1, A			; Clear RAM
	djnz Temp1, clear_ram	; Is A not zero? - jump
	; Set default programmed parameters
	call	set_default_parameters
	; Read all programmed parameters
	call read_all_eeprom_parameters
	; Set beep strength
	mov	Temp1, #Pgm_Beep_Strength
	mov	Beep_Strength, @Temp1
	; Set initial arm variable
	mov	Initial_Arm, #1
	; Initializing beep
	clr	IE_EA			; Disable interrupts explicitly
	call wait200ms	
	call beep_f1
	call wait30ms
	call beep_f2
	call wait30ms
	call beep_f3
	call wait30ms
	call	led_control

这里先做一些初始化, 特别一点这里会操作VDM0CN和RSTSRC,打开VDD电压监控,要是电压变化超过一定的范围,会软重启。

    初始化时钟CLKSEL=0, 表示div 1,  这里先是选择HFOSC0(24.5 MHz), 即1tick是 1/24.5 ns秒,后面到运行阶段要是48Mhz的MCU就会选择HFOSC1(49 MHz).

    跟着读取配置等等 然后驱动电机响三声,输出一个do re mi 的声音。表示初始化完成。

一共会有四个声调输出,f1, f2,f3,f4, 我们认为是do re mi fa吧。 即do re mi就是f1 f2  f3.

接着检查输入的rc_in, 就是电调的信线连着那个针脚的电平,检查它有没有15ms持续都是高电平,如果是的话,那么跳到bootloader, 就是进入和电脑blsuite设置的功能里。因为如 pwm是20ms一个周期,高电平最多2-3ms, dshot, ppm 等都不可能这么多时间高电平,所以这个15ms是一个很合理的值。


;**** **** **** **** **** **** **** **** **** **** **** **** ****
;
; No signal entry point
;
;**** **** **** **** **** **** **** **** **** **** **** **** ****
init_no_signal:
	; Disable interrupts explicitly
	clr	IE_EA
	; Initialize flash keys to invalid values
	mov	Flash_Key_1, #0
	mov	Flash_Key_2, #0
	; Check if input signal is high for more than 15ms
	mov	Temp1, #250
input_high_check_1:
	mov	Temp2, #250
input_high_check_2:
	jnb	RTX_PORT.RTX_PIN, bootloader_done	; Look for low
	djnz	Temp2, input_high_check_2
	djnz	Temp1, input_high_check_1

IF MCU_48MHZ == 2
	ljmp	0F000h			; Jump to bootloader
ELSE
	ljmp	1C00h			; Jump to bootloader
ENDIF

我们先不看bootloader,  先看正常流程,正常流程是跳到bootloader_done,跳到这里,就是解码之前读出的配置,  然后调用 find_throttle_gains 去找油门补偿,就是根据最大最小测试,还有死区,计算一个油门的补偿,结果放在 Throttle_Gain:    (  ; Gain to be applied to RCP value)和
Throttle_Gain_M:           (  倍数, Gain to be applied to RCP value (multiplier 0=1x, 1=2x, 2=4x etc)) )

bootloader_done:
	; Decode settings
	call	decode_settings
	; Find throttle gain from stored min and max settings
	call	find_throttle_gains
	; Set beep strength
	mov	Temp1, #Pgm_Beep_Strength
	mov	Beep_Strength, @Temp1
	; Switch power off
	call	switch_power_off
	; Set clock frequency
IF MCU_48MHZ >= 1
	Set_MCU_Clk_24MHz
ENDIF

开启油门输入的中断,开启timer等等

; Setup timers for pwm input
	mov	IT01CF, #RTX_PIN	; Route RCP input to INT0
	mov	TCON, #11h		; Timer 0 run and INT0 edge triggered
	mov	CKCON0, #04h		; Timer 0 clock is system clock
	mov	TMOD, #09h		; Timer 0 set to 16bits and gated by INT0
	mov	TMR2CN0, #04h		; Timer 2 enabled
	mov	TMR3CN0, #04h		; Timer 3 enabled
	Initialize_PCA			; Initialize PCA
	Set_Pwm_Polarity		; Set pwm polarity
	Enable_Power_Pwm_Module	; Enable power pwm module
	Enable_Damp_Pwm_Module	; Enable damping pwm module
	; Enable interrupts
IF MCU_48MHZ == 0
	mov	IE, #21h			; Enable timer 2 interrupts and INT0 interrupts
ELSE
	mov	IE, #23h			; Enable timer 0, timer 2 interrupts and INT0 interrupts
ENDIF
	mov	EIE1, #90h		; Enable timer 3 and PCA0 interrupts
	mov	IP, #01h			; High priority to INT0 interrupts

初始化比较器和ADC,并打开所有的中断

	; Initialize comparator
	Initialize_Comparator	; Initialize comparator
	; Initialize ADC
	Initialize_Adc			; Initialize ADC operation
	call	wait1ms
	setb	IE_EA			; Enable all interrupts

重置一些和dshot输入信号相关的变量:

	; Reset stall count
	mov	Stall_Cnt, #0
	; Initialize RC pulse
	clr	Flags2.RCP_UPDATED		 	; Clear updated flag
	call wait200ms
	; Clear all shot flags
	clr	Flags2.RCP_ONESHOT125			; Clear OneShot125 flag
	clr	Flags2.RCP_ONESHOT42			; Clear OneShot42 flag
	clr	Flags2.RCP_MULTISHOT			; Clear Multishot flag
	clr	Flags2.RCP_DSHOT				; Clear DShot flag
	mov 	Dshot_Cmd, #0					; Clear Dshot command
	mov 	Dshot_Cmd_Cnt, #0				; Clear Dshot command count

测试信号是不是常规的pwm, 直接等100ms ,是如果Rcp_Outside_Range_Cnt小于10,那么就是常规的pwm。  否则去检查是不是OneShot125,mulitshot. 检查之前会上面几行已经清空Flags2.RCP_ONESHOT125,  Flags2.RCP_ONESHOT42,clr    Flags2.RCP_MULTISHOT

和clr    Flags2.RCP_DSHOT。

Rcp_Outside_Range的是在输入信号的中断里,检查到高电水大于2235us就会增加一次计数。pwm是20ms一个周期,最多5次,正常一般正电平也不会超2235us。

; Test whether signal is regular pwm
	mov	Rcp_Outside_Range_Cnt, #0		; Reset out of range counter
	call wait100ms						; Wait for new RC pulse
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check how many pulses were outside normal range ("900-2235us")
	subb	A, #10						
	jnc	($+4)   ;jump to Test whether signal is OneShot125 
	ajmp	validate_rcp_start

检查输入出信号是不是dshot, 检查之前设置    Flags2.RCP_ONESHOT125  

	; Test whether signal is OneShot125
	setb	Flags2.RCP_ONESHOT125			; Set OneShot125 flag
	mov	Rcp_Outside_Range_Cnt, #0		; Reset out of range counter
	call wait100ms						; Wait for new RC pulse
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check how many pulses were outside normal range ("900-2235us")
	subb	A, #10
	jnc	($+4)
	ajmp	validate_rcp_start

检查其它信号,这里如下,这里不详细看了, 相关资料

Standard PWM :1000us – 2000us
Oneshot125:125us – 250us
Oneshot 42:2us – 84us
Multishot:5us – 25us

Dshot150 :106.8us
Dshot300 :53.4us
Dshot600 :26.7us
DShot1200:13.4us

; Test whether signal is OneShot42
	clr	Flags2.RCP_ONESHOT125
	setb	Flags2.RCP_ONESHOT42			; Set OneShot42 flag
	mov	Rcp_Outside_Range_Cnt, #0		; Reset out of range counter
	call wait100ms						; Wait for new RC pulse
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check how many pulses were outside normal range ("900-2235us")
	subb	A, #10
	jnc	($+4)
	ajmp	validate_rcp_start

	; Setup timers for DShot
	mov	IT01CF, #(80h+(RTX_PIN SHL 4)+(RTX_PIN))	; Route RCP input to INT0/1, with INT1 inverted
	mov	TCON, #51h		; Timer 0/1 run and INT0 edge triggered
	mov	CKCON0, #01h		; Timer 0/1 clock is system clock divided by 4 (for DShot150)
	mov	TMOD, #0AAh		; Timer 0/1 set to 8bits auto reload and gated by INT0
	mov	TH0, #0			; Auto reload value zero
	mov	TH1, #0
	; Setup interrupts for DShot
	clr	IE_ET0			; Disable timer 0 interrupts
	setb	IE_ET1			; Enable timer 1 interrupts
	setb	IE_EX1			; Enable int1 interrupts
	; Setup variables for DSshot150
IF MCU_48MHZ >= 1
	mov	DShot_Timer_Preset, #128			; Load DShot sync timer preset (for DShot150)
ELSE
	mov	DShot_Timer_Preset, #192
ENDIF
	mov	DShot_Pwm_Thr, #20				; Load DShot qualification pwm threshold (for DShot150)
	mov	DShot_Frame_Length_Thr, #80		; Load DShot frame length criteria
	; Test whether signal is DShot150
	clr	Flags2.RCP_ONESHOT42
	setb	Flags2.RCP_DSHOT
	mov	Rcp_Outside_Range_Cnt, #10		; Set out of range counter
	call wait100ms						; Wait for new RC pulse
	mov	DShot_Pwm_Thr, #16				; Load DShot regular pwm threshold
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check if pulses were accepted
	subb	A, #10
	mov 	Dshot_Cmd, #0
	mov 	Dshot_Cmd_Cnt, #0
	jc	validate_rcp_start

	; Setup variables for DShot300
	mov	CKCON0, #0Ch					; Timer 0/1 clock is system clock (for DShot300)
IF MCU_48MHZ >= 1
	mov	DShot_Timer_Preset, #0			; Load DShot sync timer preset (for DShot300)
ELSE
	mov	DShot_Timer_Preset, #128
ENDIF
	mov	DShot_Pwm_Thr, #40				; Load DShot qualification pwm threshold (for DShot300)
	mov	DShot_Frame_Length_Thr, #40		; Load DShot frame length criteria
	; Test whether signal is DShot300
	mov	Rcp_Outside_Range_Cnt, #10		; Set out of range counter
	call wait100ms						; Wait for new RC pulse
	mov	DShot_Pwm_Thr, #32				; Load DShot regular pwm threshold
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check if pulses were accepted
	subb	A, #10
	mov 	Dshot_Cmd, #0
	mov 	Dshot_Cmd_Cnt, #0
	jc	validate_rcp_start

	; Setup variables for DShot600
	mov	CKCON0, #0Ch					; Timer 0/1 clock is system clock (for DShot600)
IF MCU_48MHZ >= 1
	mov	DShot_Timer_Preset, #128			; Load DShot sync timer preset (for DShot600)
ELSE
	mov	DShot_Timer_Preset, #192
ENDIF
	mov	DShot_Pwm_Thr, #20				; Load DShot qualification pwm threshold (for DShot600)
	mov	DShot_Frame_Length_Thr, #20		; Load DShot frame length criteria
	; Test whether signal is DShot600
	mov	Rcp_Outside_Range_Cnt, #10		; Set out of range counter
	call wait100ms						; Wait for new RC pulse
	mov	DShot_Pwm_Thr, #16				; Load DShot regular pwm threshold
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check if pulses were accepted
	subb	A, #10
	mov 	Dshot_Cmd, #0
	mov 	Dshot_Cmd_Cnt, #0
	jc	validate_rcp_start

	; Setup timers for Multishot
	mov	IT01CF, #RTX_PIN	; Route RCP input to INT0
	mov	TCON, #11h		; Timer 0 run and INT0 edge triggered
	mov	CKCON0, #04h		; Timer 0 clock is system clock
	mov	TMOD, #09h		; Timer 0 set to 16bits and gated by INT0
	; Setup interrupts for Multishot
	setb	IE_ET0			; Enable timer 0 interrupts
	clr	IE_ET1			; Disable timer 1 interrupts
	clr	IE_EX1			; Disable int1 interrupts
	; Test whether signal is Multishot
	clr	Flags2.RCP_DSHOT
	setb	Flags2.RCP_MULTISHOT			; Set Multishot flag
	mov	Rcp_Outside_Range_Cnt, #0		; Reset out of range counter
	call wait100ms						; Wait for new RC pulse
	clr	C
	mov	A, Rcp_Outside_Range_Cnt			; Check how many pulses were outside normal range ("900-2235us")
	subb	A, #10
	jc	validate_rcp_start

如果都不满足,那么跳回no signal,继续检查。

	ajmp	init_no_signal

要是检测到上述任一个信号,那么再等3 ms, 看看jb    Flags2.RCP_UPDATED,是不是有设置,应该就是说再去读一个输入值了吧。就是没有设置,那么跳回init_no_signal, 继续检查。


validate_rcp_start:
	; Validate RC pulse
	call wait3ms						; Wait for new RC pulse
	jb	Flags2.RCP_UPDATED, ($+6)		; Is there an updated RC pulse available - proceed
	ljmp	init_no_signal					; Go back to detect input signal

信号ok了,那么开始一个长beep  f1了 , 这里没有wait30ms, 其实就是do加长三倍。

; Beep arm sequence start signal
	clr 	IE_EA						; Disable all interrupts
	call beep_f1						; Signal that RC pulse is ready
	call beep_f1
	call beep_f1
	setb	IE_EA						; Enable all interrupts
	call wait200ms	

开启arming序列, 要是dshot协议, 或者是双向打开,那么不会进行油门校准,直接跳到program_by_tx_checked, 如果设置没有打开tx program这里也会跳到program_by_tx_checked。

	; Arming sequence start
arming_start:
	jb	Flags2.RCP_DSHOT, ($+6)	; Disable tx programming for DShot
	jnb	Flags3.PGM_BIDIR, ($+6) ; Disable  tx programming for bidir

	ljmp	program_by_tx_checked	; Disable tx programming if bidirectional operation

	call wait3ms
	mov	Temp1, #Pgm_Enable_TX_Program; Start programming mode entry if enabled
	mov	A, @Temp1				
	clr	C
	subb	A, #1				; Is TX programming enabled?
	jnc 	arming_initial_arm_check	; Yes - proceed

	jmp	program_by_tx_checked	; No - branch

进行arming_initial_arm_check,Initial_Arm(这个变量在开始是1, 在arming完成之后会置0)是不是1, 要是零,也跳到program_by_tx_checked

arming_initial_arm_check:
	mov	A, Initial_Arm			; Yes - check if it is initial arm sequence
	clr	C
	subb	A, #1				; Is it the initial arm sequence?
	jnc 	arming_check			; Yes - proceed

	jmp 	program_by_tx_checked	; No - branch

开始arming_check, 找出补偿,然后等100ms, 看看New_Rcp是不是超过中位(255/2),如果不是的话,那么也跳到program_by_tx_checked。否则等1ms, 然后beep_f4,  上面这个循环两次(throttle_high_cal开始)。

就是说你的油门要是一直在高位,你听到两个f4的beep.

两声beep之后,开计算平均油门。然后等200ms, 开始响成功beep,这个是f1,f2,f3,f4等10ms, 重复三次。  这个代码在inc里。

保存这个油门值在 Pgm_Max_Throttle

arming_check:
	; Initialize flash keys to valid values
	mov	Flash_Key_1, #0A5h
	mov	Flash_Key_2, #0F1h
	; Throttle calibration and tx program entry
	mov	Temp8, #2				; Set 1 seconds wait time
throttle_high_cal:			
	setb	Flags2.RCP_FULL_RANGE	; Set range to 1000-2020us
	call	find_throttle_gains		; Set throttle gains
	call wait100ms				; Wait for new throttle value
	clr	IE_EA				; Disable interrupts (freeze New_Rcp value)
	clr	Flags2.RCP_FULL_RANGE	; Set programmed range
	call	find_throttle_gains		; Set throttle gains
	clr	C
	mov	A, New_Rcp			; Load new RC pulse value
	subb	A, #(255/2)			; Is RC pulse above midstick?
	setb	IE_EA				; Enable interrupts
	jc	program_by_tx_checked	; No - branch

	call wait1ms		
	clr	IE_EA				; Disable all interrupts
	call beep_f4
	setb	IE_EA				; Enable all interrupts
	djnz	Temp8, throttle_high_cal	; Continue to wait

	call	average_throttle
	clr	C
	mov	A, Temp8
	mov	Temp1, #Pgm_Max_Throttle	; Store
	mov	@Temp1, A			
	call wait200ms				
	call	success_beep

然后等用户打遥杆输入低油门, 找出补偿,然后等100ms, 看看New_Rcp是不是低于 中位(255/2),不是的话,在跳回throttle_low_cal循环续等。

等到小于中位的油门,就响beep_f1两次。

比较当前油门(new_Rcp)值是不是 Pgm_Max_Throttle 小 140us(35),如果太小,就是会把Pgm_Max_Throttle设置为当前的最小值+140us(35),然后等200ms.

 响成功的声音 success_beep_inverted, 这个和高油门的反过来了,就是f4, f3, f2, f1这样响三次,也是在inc文件里,这里为会了不打断主流程也不列贴出来了。


throttle_low_cal_start:
	mov	Temp8, #10			; Set 3 seconds wait time
throttle_low_cal:			
	setb	Flags2.RCP_FULL_RANGE	; Set range to 1000-2020us
	call	find_throttle_gains		; Set throttle gains
	call wait100ms
	clr	IE_EA				; Disable interrupts (freeze New_Rcp value)
	clr	Flags2.RCP_FULL_RANGE	; Set programmed range
	call	find_throttle_gains		; Set throttle gains
	clr	C
	mov	A, New_Rcp			; Load new RC pulse value
	subb	A, #(255/2)			; Below midstick?
	setb	IE_EA				; Enable interrupts
	jnc	throttle_low_cal_start	; No - start over

	call wait1ms		
	clr	IE_EA				; Disable all interrupts
	call beep_f1
	call wait10ms
	call beep_f1
	setb	IE_EA				; Enable all interrupts
	djnz	Temp8, throttle_low_cal	; Continue to wait

	call	average_throttle
	mov	A, Temp8
	add	A, #3				; Add about 1%
	mov	Temp1, #Pgm_Min_Throttle	; Store
	mov	@Temp1, A			
	mov	Temp1, A				; Min throttle in Temp1
	mov	Temp2, #Pgm_Max_Throttle
	mov	A, @Temp2
	clr	C
	subb	A, #35				; Subtract 35 (140us) from max throttle
	jc	program_by_tx_entry_limit
	subb	A, Temp1				; Subtract min from max
	jnc	program_by_tx_entry_store

program_by_tx_entry_limit:
	mov	A, Temp1				; Load min
	add	A, #35				; Make max 140us higher than min
	mov	Temp1, #Pgm_Max_Throttle	; Store new max
	mov	@Temp1, A

program_by_tx_entry_store:
	call wait200ms				
	call erase_and_store_all_in_eeprom	
	call	success_beep_inverted

然后来到tx编程的等待,这里又会跳回去init_no_signal,这里又会走一次上面的设置代码,但是到了校准高位油门时,因为New_Rcp这时是小于255/2的,所以你听到一声长响的beep_f1(do) 后,就直接会跳到了program_by_tx_checked.

program_by_tx_entry_wait:
	call wait100ms
	call	find_throttle_gains		; Set throttle gains
	ljmp	init_no_signal			; Go back

检查油门是不是低于stop, 是的话,arming结束, 开始长响一个三拍的beep_f4(没有wait,所以连起来了), 保存 Initial_Arm为0.

program_by_tx_checked:
	; Initialize flash keys to invalid values
	mov	Flash_Key_1, #0
	mov	Flash_Key_2, #0
	call wait100ms				; Wait for new throttle value
	clr	C
	mov	A, New_Rcp			; Load new RC pulse value
	subb	A, #1				; Below stop?
	jc	arm_end_beep			; Yes - proceed

	jmp	arming_start			; No - start over

arm_end_beep:
	; Beep arm sequence end signal
	clr 	IE_EA				; Disable all interrupts
	call beep_f4				; Signal that rcpulse is ready
	call beep_f4
	call beep_f4
	setb	IE_EA				; Enable all interrupts
	call wait200ms

	; Clear initial arm variable
	mov	Initial_Arm, #0

检查信号, 这个其实就是看看 Rcp_Timeout_Cnt是不是零,Rcp_Timeout_Cntd在开始赋值1或10,要是超时这个减少1,要是零表示,有一段时间没有收到油门信号变化了,Timer2(t2), 每32ms会去减一下Rcp_Timeout_Cnt直至到零。Pgm_Beacon_Delay会和Power_On_Wait_Cnt_H相减,要是借位,就会开始beep f4, beep一次Power_On_Wait_Cnt_H会减1,就是Power_On_Wait_Cnt_L是255的时候beep一次了。 这个是用来发出声音来寻机用的。很多地方会跳回wait_for_power_on。

	; Armed and waiting for power on
wait_for_power_on:
	clr	A
	mov	Power_On_Wait_Cnt_L, A	; Clear wait counter
	mov	Power_On_Wait_Cnt_H, A	
wait_for_power_on_loop:
	inc	Power_On_Wait_Cnt_L		; Increment low wait counter
	mov	A, Power_On_Wait_Cnt_L
	cpl	A
	jnz	wait_for_power_on_no_beep; Counter wrapping (about 3 sec) , jump if  A nozero, cpl first, actually jmp if A is not 255

	inc	Power_On_Wait_Cnt_H		; Increment high wait counter
	mov	Temp1, #Pgm_Beacon_Delay
	mov	A, @Temp1
	mov	Temp1, #25		; Approximately 1 min
	dec	A
	jz	beep_delay_set

	mov	Temp1, #50		; Approximately 2 min
	dec	A
	jz	beep_delay_set

	mov	Temp1, #125		; Approximately 5 min
	dec	A
	jz	beep_delay_set

	mov	Temp1, #250		; Approximately 10 min
	dec	A
	jz	beep_delay_set

	mov	Power_On_Wait_Cnt_H, #0		; Reset counter for infinite delay

beep_delay_set:
	clr	C
	mov	A, Power_On_Wait_Cnt_H
	subb	A, Temp1				; Check against chosen delay
	jc	wait_for_power_on_no_beep; Has delay elapsed?

	call	switch_power_off		; Switch power off in case braking is set
	call	wait1ms
	dec	Power_On_Wait_Cnt_H		; Decrement high wait counter
	mov	Power_On_Wait_Cnt_L, #0	; Set low wait counter
	mov	Temp1, #Pgm_Beacon_Strength
	mov	Beep_Strength, @Temp1
	clr 	IE_EA				; Disable all interrupts
	call beep_f4				; Signal that there is no signal
	setb	IE_EA				; Enable all interrupts
	mov	Temp1, #Pgm_Beep_Strength
	mov	Beep_Strength, @Temp1
	call wait100ms				; Wait for new RC pulse to be measured

wait_for_power_on_no_beep:
	call wait10ms
	mov	A, Rcp_Timeout_Cntd			; Load RC pulse timeout counter value
	jnz	wait_for_power_on_not_missing	; If it is not zero - proceed

	jmp	init_no_signal				; If pulses missing - go back to detect input signal

如果有信号(Rcp_Timeout_Cntd不是零),同时New_Rcp(油门)不是零跳到 wait_for_power_on_nonzero, 再检查一次Rcp_Timeout_Cntd是不是零,要是不是零,那就跳到init_start

wait_for_power_on_not_missing:
	clr	C
	mov	A, New_Rcp			; Load new RC pulse value
	subb	A, #1		 		; Higher than stop
	jnc	wait_for_power_on_nonzero	; Yes - proceed

	clr	C
	mov	A, Dshot_Cmd
	subb	A, #1		 		; 1 or higher
	jnc	check_dshot_cmd		; Check Dshot command

	ljmp	wait_for_power_on_loop	; If not Dshot command - start over

wait_for_power_on_nonzero:
	lcall wait100ms			; Wait to see if start pulse was only a glitch
	mov	A, Rcp_Timeout_Cntd		; Load RC pulse timeout counter value
	jnz	($+5)				; If it is not zero - proceed
	ljmp	init_no_signal			; If it is zero (pulses missing) - go back to detect input signal

	mov 	Dshot_Cmd, #0
	mov 	Dshot_Cmd_Cnt, #0
	ljmp init_start

上面基本上是准备阶段,下一篇 我们继续读init_start ,  开始驱动电机去工作了。 未完待续!

【Quad FPV】BLDC:BLHeli_S从硬件PCB设计到Keil编译、下载 前言 大学接触到多旋翼无人机以来,飞行水平未见提高,倒是一直在关注遥控设计和机体设计方面。本文记录关于BLHeli电调设计开发过程中的一些坑。 参考文档: timegate 墨鸢,moyuan2000@163.com《无感无刷直流电机之电调设计全攻略 》 逗倪豌儿,联系方式现已失效,《编译 BLHeli_S 的正确方式》 BLHeli固件开源地址:https://github.com/bitdump/BLHeli.git BLHeli上位机:https://github.com/blheli-config 阅读详情

相关推荐

(11)(2.2) BLHeli32 and BLHeli_S ESCs(一)

Ardupilot官网关于《Peripheral Hardware》的翻译 —《(2.2) BLHeli32 and BLHeli_S ESCs》。

无人机开发(开源Ardupilot) 2万+

AM32电调学习笔记(1)——IO.c

由于目前无刷电调学习资源较少,笔者找到的学习的资源(按学习的顺序)有正点原子电机教程、CW32开源无刷电机开发板、逐飞开源无刷电调,BLHeli,AM32。逐飞CHV203电调方案通过三个定时器达到了精准换相的效果,对作为初学者的我而言是一份相当好的学习资源。而AM32作为目前主流的32位电调方案,支持软件调参,正弦启动,双向Dshot等等非常好的特性。正点原子的无刷算法较为简单,使用单定时器,难以实现极高转速下的精准换相。CW32使用的方案为ADC采样,对新手难度而言,学习难度较大。

qq_42849141的博客 2061

从零到一:开源BLHeli_S电调DIY全流程实战解析

本文详细解析了开源BLHeli_S无刷电调的DIY全流程,从核心器件选型(EFM8BB21、FD6288)到硬件设计、固件烧录与参数配置,再到测试验证与性能优化。通过实战案例,帮助读者掌握无刷电调开发的关键技术,适用于无人机和航模爱好者提升DIY能力。

weixin_29191669的博客 644

开源BLHELI-S 代码详细解读(二)

DIR_CHANGE_BRAKE没有设置的话,就跳到run_to_wait_for_power_on,不是的话,清除DIR_CHANGE_BRAKE的标志,清除PGM_DIR_REV标志,要是RCP_DIR_REV有设置,那么设置PGM_DIR_REV,然后把当前的阶段设置回INITIAL_RUN_PHASE。run_to_wait_for_power_on_fail是由前面的wait_for_comp_out_low和wait_for_comp_out_high的异常读取触发的。

aerror的专栏 3090

立创开源Blheli_s 8S60A电调:基于BLHeli_s固件的大功率无感方波驱动方案解析

本文详细解析了立创开源Blheli_s 8S60A电调方案,这是一款基于BLHeli_s固件的大功率无感方波驱动方案。文章深入介绍了其硬件规格,包括支持8S锂电池、峰值60A的强劲性能,并重点解析了其支持的Dshot等丰富控制协议。同时,提供了使用Arduino Nano作为编程器进行固件烧录的完整步骤指南,适用于大型无人机、模型船等大功率应用场景。

weixin_30021053的博客 385

BLHeli最新源码

BLHeli的最新源码,可以烧录到EFM8BB21/20系列的单片机中,飞穿越记

【BLHeli固件】好盈乐天电调刷BLHeli固件调参 arduino

使用BLHeliSuite,通过arduino开发板给好盈乐天电调刷固件

qq_42705768的博客 5592

安卓-折叠屏平行视界适配(多厂商兼容与优先级实战)

本文详细解析了安卓折叠屏平行视界适配的现状与挑战,重点探讨了多厂商兼容性策略和优先级冲突的实战解决方案。通过对比Google的Embedding方案和国内厂商的EasyGo标准,提供了检测设备支持方案、处理优先级冲突及双方案共存的最佳实践,帮助开发者高效完成折叠屏适配。

weixin_29190169的博客 162

BLHeli 固件开源项目教程

BLHeli 固件开源项目教程 项目地址:https://gitcode.com/gh_mirrors/bl/BLHeli 1. 项目介绍 BLHeli 是一个专为无刷电子速度控制器(ESC)固件开发的开源项目。它最初由 Assembly 编写,支持 Atmel 和 Silabs 的 8 位微控制器。后来发展出 BLHeli_S 和 BLHeli_32 两个版本,分别面向 8 位和 32 位处理器...

gitblog_01155的博客 1565

c语言版数据结构课程设计 学生成绩管理系统

此学生成绩管理系统完成15项功能,是我经过很多次的修改得到的。花费时间不短,功能齐全,主要完成了:输入,输出,插入,修改,删除,查询,读取文件,写入文件,复制文件,建立索引,显示各种情况下的信息,追加学生信息等等功能。一共15项。   代码如下: // shujujiegou.cpp : 定义控制台应用程序的入口点。///**本程序采用数组链表形式,原因:数组查询速度快,数组也能实现动态存储

Java 1万+

vue3的面试题

vue3的面试题

jiang7701037的博客 6264

开源BLHELI-S 代码详细解读(三)

如果不满足,则跳到run_to_wait_for_power_on_fail,这里上一篇有详细读了,是一些失败的检查,最坏会跳回init_no_signal。B. 否则就是在启动的阶段(STARTUP_PHASE),这里会将Startup_Zc_Timeout_Cntd减1(超时次数,wait_before_zc_scan会把它设置成2),要是它还不是0,就跳到comp_check_timeout_extend_timeout,再调用setup_zc_scan_timeout。

aerror的专栏 1677

阿里云网络架构设计

背景 一个企业上云,首先要考虑整个网络的规划,涉及到云上和云下的网络规划,云上VPC的规划,云上不同地域VPC的互通,云上可用区的选择等等,所以一定要提前规划好整体的网络架构,现在阿里云云上的虚拟化网络解决方案已经完全转向 了云企业网,所以所有使用阿里云的企业客户多多少少都会使用阿里云的CEN 支撑自己的业务。 云企业网介绍 云企业网(Cloud Enterprise Network)是承载在阿里云提供的高性能、低延迟的私有全球网络上的一张高可用网络。 IDC专线接入CEN 首先面临的是云上和云下打通,那么

修电脑的码农 4838

【亲测免费】 推荐开源项目:KEIL编译BLHeli_S正确方式指南

推荐开源项目:KEIL编译BLHeli_S正确方式指南 【下载地址】KEIL编译BLHeli_S正确方式指南 KEIL编译BLHeli_S正确方式指南本仓库提供了一个名为“KEIL编译BLHeli_S正确方式.pdf”的资源文件,该文件详细介绍了如何使用KEIL MDK编译著名的开源电调BLHeli_S工程 ...

gitblog_06688的博客 1076

Matlab实现POD本征正交分解数据降维模型

近年来,随着大数据时代的到来,各种复杂的工程问题产生了海量的高维数据,如何有效降维、提取数据中的关键信息成为了众多领域中的重要问题。在传统的数据降维方法中,PCTFA(主成分分析)是一种典型的统计方法,然而对于流体动力学等非线性问题,PCTFA在处理时可能失去部分物理意义,难以捕捉到系统的复杂性。在降维过程中,POD提取的主要特征通常是一组复杂的正交基函数,如何将这些结果有效地转化为直观且易于理解的物理意义,特别是在实际应用中,对该特征的理解和分析,依然是一个重要的课题。本项目的扩展性也得到了有效体现。

优质创作者: 人工智能技术领域 对于有一定基础的 MATLAB和Python带具体需求都可以详谈 MATLAB和Python可以提供代码调试服务加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢 1216

制作Ubuntu22.04启动盘

现在越来越多程序代码都需要运行在linux环境下,运行这些代码的前提是安装一个稳定的linux操作系统。本文选择Ubuntu 22.04去制作系统启动盘。本文主要对ubuntu22.04的启动盘制作过程进行了详细的说明,希望能帮助到有需要的朋友。

lyx_ok的博客 1万+

BLHeli开源电调固件:从新手到专家的完整应用指南

BLHeli是一款专为无刷电机电调设计的开源固件,支持多种飞行器应用场景。作为航模爱好者和无人机开发者的首选方案,它提供了灵活的配置选项和稳定的性能表现。 ## 🚀 三大固件版本:选择适合你的技术路线 BLHeli生态包含三个主要版本,每个版本针对不同的硬件平台和应用需求: ### 1. 经典8位MCU版本(BLHeli) - **支持平台**:Atmel和Silabs 8位微控制器 -

gitblog_01174的博客 915

【AI应用实战-OpenCode】OpenCode安装部署(一)

OpenCode是一款2026年流行的开源AI编程工具,兼具轻量代码编辑器和智能编码代理功能。其核心优势包括:完全免费开源(MIT协议)、全平台兼容、支持75+大模型(含本地部署)、内置完整编程功能链(代码补全/Git集成/AI调试等),且所有数据本地存储确保隐私安全。相比Copilot等商业工具,OpenCode提供更自由的模型选择和零门槛使用体验。安装要求Windows10+/macOS12+/Linux主流发行版,仅需200MB空间和4G内存。该工具完美兼容VSCode生态,支持终端/桌面/IDE三种

2425

BLHeli_S与BLHeli_32终极对比:无刷电调固件深度解析

在无人机和遥控模型领域,BLHeli固件已经成为无刷电调(ESC)的事实标准。作为开源固件的先驱,BLHeli项目经历了从8位到32位架构的演进,形成了BLHeli_S和BLHeli_32两大主流分支。本文将从技术架构、性能表现、适用场景到实战配置进行全面对比,帮助技术爱好者和进阶用户做出明智选择。 ## 架构演进:从8位精简到32位全能 BLHeli固件的演进反映了嵌入式系统发展的典型路径。

gitblog_00252的博客 374

若依框架二次开发——RuoYi-AI 本地部署流程

若依框架二次开发——RuoYi-AI 本地部署流程

bjzhang75的博客 1784

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

开源BLHELI-S 代码详细解读(四)

如Set_Pwm_C, P1SKIP = 37h(00110111b), 即P1.3 P1.6和 P1.7会给外设使用, 根据优先度,PCA0的CEX0会使用P1.3(CpwmFET)输出 CEX1 会在P1.6(CcomFET)输出。这个 wait_before_zc_scan,其实比较简单,就是从把timer3的超时设置为Comm_Period4x_L和Comm_Period4x_H, 只有在STARTUP_PHASE和 SHL INITIAL_RUN_PHASE阶段才会设置。

aerror的专栏 1262

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

全面解析BLHeli开源电调固件:从8位到32位的无刷电机控制演进

BLHeli是一款广泛应用于无人机和航模领域的开源无刷电调固件,为多旋翼飞行器提供高效精准的电机控制解决方案。这款固件经历了从最初的8位Atmel平台到高性能32位ARM架构的完整演进,成为业界最受欢迎的电调控制软件之一。 ## 🔧 架构演进:三款固件的技术路线图 BLHeli项目包含了三个主要版本,每个版本都针对不同的硬件平台和应用场景进行了优化: **BLHeli(经典版)** - 基

gitblog_00164的博客 266

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Bluejay电调固件介绍和烧录Bluejay电调固件到BLHeli_S电调的方法

本篇文章介绍了Bluejay电调固件介绍和烧录Bluejay电调固件到BLHeli_S电调的方法。Bluejay是一种数字电调固件,用于控制多旋翼无人机中的无刷电机。该电调固件基于BLHeli_S电调固件revision 16.7版本进行改进的。Bluejay的目标是成为BLHeli_S的开源继任者,通过Busy Bee MCU对电调进行了一些改进。DShot是唯一受支持的电调协议。支持用户配置电调启动音乐。Bluejay固件可以烧录到与BLHeli_S兼容的电调。

在后厂村路努力科研搬砖的无人机控制方向练习生一名~ 8807

BLHeli电调固件终极指南:5个秘诀解锁无刷电机极致性能

BLHeli是一款开源的**无刷电调固件**,专为航模爱好者和无人机开发者设计。这个强大的固件项目支持多种硬件平台,包括Atmel和SiLabs微控制器,能够显著提升电调性能和响应速度。无论你是竞速飞手还是航拍爱好者,掌握**BLHeli电调调参**技巧都能让你的飞行器性能达到新高度!🚀 ## 项目概述与核心价值 BLHeli项目提供了三个主要版本:经典的BLHeli、改进的BLHeli_S

gitblog_00264的博客 434

深度解析:BLHeli无刷电调固件架构对比与性能优化指南

BLHeli作为开源无刷电调固件的领先解决方案,为无人机和RC模型提供了高性能的电机控制技术。本文将从技术架构、性能参数和应用场景三个维度,深入对比BLHeli、BLHeli_S和BLHeli_32三款固件的核心差异,帮助开发者选择最适合的无刷电调固件方案。 ## 技术背景与项目概述 BLHeli项目始于2011年,最初为Atmel和Silabs 8位MCU开发,采用汇编语言编写,支持直升机主

gitblog_00433的博客 615

BLHeli电调固件配置终极指南:3分钟快速入门教程

BLHeli是一款开源的无刷电调固件项目,为航模爱好者提供了强大的电机控制解决方案。无论你是新手还是经验丰富的飞手,掌握**BLHeli电调固件配置**技巧都能显著提升飞行器的性能和响应速度。这个项目包含了BLHeli、BLHeli_S和BLHeli_32三个主要版本,支持多种硬件平台,为你的飞行器提供专业级的电机控制能力。 ## 为什么选择BLHeli电调固件? BLHeli固件相比原厂固件

gitblog_00622的博客 642
上一篇: 使用GDB动态补丁ARM64的程序 跳转到任意地址
下一篇: 开源BLHELI-S 代码详细解读(二)
aerror
博客等级 码龄23年 392粉丝 193原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值