逗倪豌儿 发表于 2016-12-24 10:39:05

BLHeli_S 源码解析讨论

# 1.时钟配置说明 #

对每次 FLASH 读或取指操作,系统为 FLASH存储器提供一个内部 FLASH 读选通信号。FLASH读选通信号持续一或两个系统时钟周期,由 FLRT(PFE0CN.4)决定。如果系统时钟大于 25 MHz,则 FLRT 位必须被设置为逻辑 1,否则,从 FLASH读取的数据或指令不是实际的 FLASH内容。

更新 FLRT步骤如下

- 第一步:设置SFRPAGE地址为 PFE0CN页地址(0X10) 即 mov SFRPAGE, #10h
- 第二步:

         如果系统时钟SYSCLK小于等于25M 则
                         禁止指令预取引擎(PFE0CN寄存器中的 PFEN位清 0),设置PFE0CN.5 = 0
                         FLASH读选通信号为一个系统时钟                   设置 PFE0CN.4 = 0即mov SFRPAGE, #10h

         如果系统时钟SYSCLK大于 25 MH 则
                         使能指令预取引擎(PFE0CN寄存器中的 PFEN位清 0),设置PFE0CN.5 = 1
                         FLASH读选通信号为两个系统时钟                   设置 PFE0CN.4 = 1 即mov PFE0CN, #30h

- 第三步:设置 SFRPAGE页地址为 00H。即 mov SFRPAGE, #00h

所以时钟配置程序如下 :
    ;****************************** 设置系统时钟为24M *****************************
    ;
    ; 1:时钟源为 HFOSC1 (48M),然后配置为 2分频
    ; 2
    ;
    ;*****************************************************************************
    Set_MCU_Clk_24MHz MACRO
    mov CLKSEL, #13h; Set clock to 24MHz
    mov SFRPAGE, #10h
    mov PFE0CN, #00h; Set flash timing for 24MHz
    mov SFRPAGE, #00h
    mov Clock_Set_At_48MHz, #0
    ENDM

   ;****************************** 设置系统时钟为48M *****************************
    ;
    ; 1:时钟源为 HFOSC1 (48M) SYSCLK 为clock source(原时钟的1分频)
    ; 1:
    ;
    ;*****************************************************************************
    Set_MCU_Clk_48MHz MACRO
    mov SFRPAGE, #10h
    mov PFE0CN, #30h; Set flash timing for 48MHz
    mov SFRPAGE, #00h
    mov CLKSEL, #03h; Set clock to 48MHz
    mov Clock_Set_At_48MHz, #1
    ENDM

##1.1页寄存器使用说明##

你所用的每个寄存器都有个寄存地址,而寄存器页的页码就是指向你的寄存地址的。当你需要使用某个寄存器时,就必须把SFRPAGE指向你所要用的寄存器页上面去
举例如下:

    void Init_ADC0()

    {

   char SFRPAGE_SAVE = SFRPAGE;// 保存当前的SFR页

   SFRPAGE = ADC0_PAGE;          //把页码改到以下几个寄存器所在的页码

   ADC0CN = 0x00;                // 每次向AD0BUSY 写1 时启动ADC0 转换

   REF0CN = 0x03;                // ADC0电压基准取自VREF0引脚,内部电压基准缓冲器工作

   AMX0CF = 0x00;                // AIN inputs are single-ended (default)

   AMX0SL = 0x00;                // 选择ADC 输入为AIN0.0   

                                  // ADC0CF = (SYSCLK/SAR_CLK) << 3; // ADC 转换时钟 = 2.5MHz

   ADC0CF |= 0x00;// PGA 增益 = 1 (默认)

                                  // EIE2 |= 0x02;   // 允许ADC0 转换结束中断

   SFRPAGE = SFRPAGE_SAVE;      //回复寄存器页

    }

这个程序中char SFRPAGE_SAVE = SFRPAGE; 和SFRPAGE = SFRPAGE_SAVE; 是使在调用这个子函数后页码能够回到调用前的页码

----------

# 2.脉冲捕获--获取油门值说明 (Get_Rcp_Capture_Values) #
函数说明

- 1:脉冲捕获使用定时器0,计数周期 41.67ns;
- 2:该函数是在定时器 0中断中执行。

操作步骤如下:

- 1:关闭定时器0
- 2:保存计数值 Temp1 = TL0Temp2 = TH0Temp3 = Timer0_X (该值在定时器0中断中被自加)
- 3:如果定时器0和其他的中断同时产生了,那么定时器0会被挂起,如果被挂起则把Temp3++, 否则 Temp3 保持原来的值
- 4:把定时器0 计数器清0 即TL0 =0 TH0 = 0
- 5:把 Timer0_X 清零,即 Timer0_X = 0
- 6:从新使能定时器 0
- 7: 如果48M Temp1 = Temp1 *2Temp2 = Temp2 *2Temp3 = Temp3 *2,如果不是 Temp1 Temp2 Temp3 保持原有值

该函数的作用是读取脉冲捕获的计数器值 参数返回放到 Temp1 和 Temp2 中


----------

# 3. PCA初始化函数说明 -- Initialize_PCA #
说明:

-1: 使能PCA0
-2: PCA0时钟选择系统时钟
-3: 如果延时为0如果48M,则设置PCA0为11位PWM,否则设置为10位模式 ,设置PWM 边沿对齐
-4: 如果延时不为0 如果48M 则设置PCA0为10位PWM,否则设置为9位模式 , 设置PWM 中间对齐

----------

# 4.上电PWM模式设置--Enable_Power_Pwm_Module #

说明:

1. 如果延时为0 使能比较器CPM0,使能匹配功能,使能PCA为PWM模式
2. 如果延时为0 使能比较器CPM0, 禁能匹配功能, 使能PCA为PWM模式

代码如下 :

Enable_Power_Pwm_Module MACRO
    IF FETON_DELAY == 0
            mov PCA0CPM0, #4Ah; Enable comparator of module, enable match, set pwm mode
    ELSE
            mov PCA0CPM0, #42h; Enable comparator of module, set pwm mode
    ENDIF
    ENDM

# ADC 初始化函数 -- Initialize_Adc #


说明:设置采样频率为 2M

-1:设置VDD作为电压基准,使能内部温度传感器
-2:设置ADC转换clk =PCLK/(REF0CN >>3 -1) ,所以,

                  如果是48M 则48/ (B9H >>3 -1)= 2.18M
                如果是24M 则24/ (59h >>3 -1)= 2.4M

-3:设置ADC增益为 1倍

代码如下 :

Initialize_Adc MACRO
            mov REF0CN, #0Ch; Set vdd (3.3V) as reference. Enable temp sensor and bias
    IF MCU_48MHZ == 0
            mov ADC0CF, #59h; ADC clock 2MHz, PGA gain 1
    ELSE
            mov ADC0CF, #0B9h   ; ADC clock 2MHz, PGA gain 1
    ENDIF
            mov ADC0MX, #10h; Select temp sensor input
            mov ADC0CN0, #80h   ; ADC enabled
            mov ADC0CN1, #01h   ; Common mode buffer enabled
    ENDM

定时器资源使用如下 :

1. 定时器 0 : 定时周期41.67ns, 用于RC;
2. 定时器 2 : 定时周期500ns,   用于RC 和换向时间;
3. 定时器 3 : 定时周期500ns,   用于换向时间;
4. PCA0    : 用于硬件PWM ;

# 读取全部eeprom函数: read_all_eeprom_parameters #

执行过程 :

从 Eep_Initialized_L 所代表的地址中读取数据dataL放到 Bit_Access所代表的地址中 ,

如果 读到数据dataL == 055h

                把 Eep_Initialized_L +1 所代表的地址中的数据读取出来dataH,放到   Bit_Access所代表的地址中,此时
             如果 dataH==0AAh 则调用read_eeprom_read函数,读取 eeprom中的数据;
             如果 dataH!= 0AAh 则调用 read_eeprom_store_defaults函数 ,读取默认的eeprom中的数据
如果 读到数据dataL != 055h

                则调用 read_eeprom_store_defaults函数 ,读取默认的eeprom中的数据

总结 :如果 = 055h 且 = 0AAh 才能调用read_eeprom_read函数,读取 eeprom中的数据;否则会调用 read_eeprom_store_defaults函数 ,读取默认的eeprom中的数据,

# 延时函数#

----------
执行一次是 42.7us内环是 23 * 42.7 us = 982.1us = 1ms
所以 Temp2的值决定着 要延时多少ms eg : wait30ms中 要把 Temp2 = 30

;**** **** **** **** **** **** **** **** **** **** **** **** ****
    ;
    ; Wait xms ~(x*4*250)(Different entry points)
    ;
    ; No assumptions
    ;
    ;**** **** **** **** **** **** **** **** **** **** **** **** ****
    wait1ms:
            mov         Temp2, #1
            jmp               waitxms_o

    wait3ms:
            mov         Temp2, #3
            jmp         waitxms_o

    wait10ms:   
            mov         Temp2, #10
            jmp         waitxms_o

    wait30ms:   
            mov         Temp2, #30
            jmp         waitxms_o

    wait100ms:
            mov         Temp2, #100
            jmp         waitxms_o

    wait200ms:
            mov         Temp2, #200
            jmp         waitxms_o

    waitxms_o:; Outer loop
            mov         Temp1, #23
    waitxms_m:; Middle loop
            clr         A
            djnz      ACC, [      DISCUZ_CODE_0      ]nbsp; ; Inner loop (42.7us - 1024 cycles)
            djnz      Temp1, waitxms_m
            djnz      Temp2, waitxms_o

    ret

# pgm_start 函数 #

1. 初始化 Flash_Key_1和Flash_Key_2为 0
2. 禁能看门口
3. 初始化栈大小
4. 使能VDD电压监视器
5. 如果 是1s电池 不把VDD电压监视器设置为系统复位源,否则设置为系统复位源
6. 选择内部震荡器为系统时钟源
7. 关闭所有的MOS管和PWM输出
8. 初始化 所有IO
9. 初始化交叉开关
10. 再次关闭所有的MOS管和PWM输出
11. 清除RAM
12. 执行设置默认参数到内存中
13. 执行从eeprom中读取参数
14. 取出Pgm_Beep_Strength地址中的数据 放到Beep_Strength中
15. 把 Initial_Arm位置1
16. 禁能所有中断,
17. 发声处理
18. 执行led控制

代码如下 :
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
            orl VDM0CN, #080h       ; Enable the VDD monitor
      IF ONE_S_CAPABLE == 0      
            mov   RSTSRC, #06h    ; Set missing clock and VDD monitor as a reset source if not 1S capable
      ELSE
            mov   RSTSRC, #04h    ; Do not set VDD monitor as a reset source for 1S ESCSs, in order to avoid resets due to it
      ENDIF
            ; 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

最后奉上BLHeli_s 电调最小电调系统原理图,支持BLHeli_S F_H_40固件,后面还需用户自行添加驱动电路,电调电流大小,取决于驱动电路的MOS型号,和最小系统无关,所以后面的驱动电路需要用户自行设计,(驱动电路都长那个样子,一般都是差不多)

逗倪豌儿 发表于 2016-12-24 11:32:07

附件是BLHeli_S keil 编译说明,源码结构分析:目录结构如所示,可以看出只有 Atmel/BLHeli_S SiLabs/SiLabs 三个文件夹有用,举例说 明:当你想用 EFM8 系列的 MCU 作为主控,那么你就可以直接拷贝 BLHeli_S SiLabs 文件夹 加入到你的工程,其他的都可以删除,同理也是一样。本教程以 BLHeli_S SiLabs 文件夹为例 说明,因为这个是最新源码,并且长久更新。

rei1984 发表于 2016-12-24 12:07:03

lzblheli    官方网站多少?github地址多少?? 研究一下 有什么优势 或者 缺点

逗倪豌儿 发表于 2016-12-24 12:18:18

rei1984 发表于 2016-12-24 12:07
lzblheli    官方网站多少?github地址多少?? 研究一下 有什么优势 或者 缺点 ...

github.com/bitdump/BLHeli

yuntian 发表于 2016-12-25 20:55:17

楼主有对应的原理图吗?另外BLHeli_S 用了硬件的PWM,以前版本在检查比较器结果时会判断是PWN_ON还是OFF,不知道这个版本是怎么做判断的?

逗倪豌儿 发表于 2016-12-26 10:07:54

yuntian 发表于 2016-12-25 20:55
楼主有对应的原理图吗?另外BLHeli_S 用了硬件的PWM,以前版本在检查比较器结果时会判断是PWN_ON还是OFF, ...

1:1楼最后的PDF就是原理图,支持BLHeli_S F_H_40 原生固件;
2:在检测比较器结果的时候,不用管PWM开或者关,作者的处理方式是根据不同的电机状态,决定检测次数,动态调整,如果检测过程中出现错误就再进行检查,直到比较器检测超时为止,如果超时就出错,否则结果是想要的,检测时间正常情况下是过零时间的3/8.

yuntian 发表于 2016-12-27 13:00:55

谢谢解答, 另外请教个问题, 硬件PWM是使用互补PWM吗?

逗倪豌儿 发表于 2016-12-27 13:22:21

yuntian 发表于 2016-12-27 13:00
谢谢解答, 另外请教个问题, 硬件PWM是使用互补PWM吗?

BLHeli_s是硬互补PWM

grash 发表于 2016-12-27 14:14:02

原来电调也是有程序的,我以为是纯模拟电路呢

逗倪豌儿 发表于 2016-12-27 16:54:56

grash 发表于 2016-12-27 14:14
原来电调也是有程序的,我以为是纯模拟电路呢

不但有程序,而且电机控制是一个大的学问!平常用的电调是一种简化版的电机控制。

yuntian 发表于 2016-12-28 17:53:39

EFM8 keil 5 的device pack 能不能发一下, 在网上没找到, 谢谢! 另外有板子可以买吗? 想直接在板子上调一下

sctwp 发表于 2017-1-2 20:56:57

密码不对

逗倪豌儿 发表于 2017-1-4 10:27:17

sctwp 发表于 2017-1-2 20:56
密码不对

What 密码?

makeflyeasy 发表于 2017-7-3 11:53:58

楼主的BLHeli的源码能发一份吗?好像官网的只有HEX文件,没有asm的源码了

huangyiting1990 发表于 2017-7-3 14:08:40

makeflyeasy 发表于 2017-7-3 11:53
楼主的BLHeli的源码能发一份吗?好像官网的只有HEX文件,没有asm的源码了

https://github.com/bitdump/BLHeli/tree/master/BLHeli_S%20SiLabs

这不是有源码吗?

makeflyeasy 发表于 2017-7-3 15:42:08

huangyiting1990 发表于 2017-7-3 14:08
https://github.com/bitdump/BLHeli/tree/master/BLHeli_S%20SiLabs

这不是有源码吗?

谢谢,之前可能找错了吧。已经下载下来了。谢谢哈

fuandtao 发表于 2017-7-28 11:33:52

您好,请问BLHeli-Skeli 工程 百度网盘的密码是多少

小小苹果 发表于 2017-8-9 11:51:41

电调水太深

sun_sky 发表于 2018-2-22 23:39:54

非常想试试做个电调,可自己做好像成本并不划算,也许只是图个乐趣

wilderujs 发表于 2018-4-18 07:36:45

厉害,学习学习

wuyoujr 发表于 2019-7-26 09:55:51

麻烦问一下网盘keil工程的密码多少啊{:smile:}

makathy 发表于 2019-7-26 12:56:52

学习一下

motor_control 发表于 2019-7-26 19:40:34

做电调开发需要相当知识和经验,拿开源学习很不错,做产品还是三思而行。

HopeTu 发表于 2019-9-18 17:40:32

谢谢分享
页: [1]
查看完整版本: BLHeli_S 源码解析讨论