分类目录归档:STM8

【BYSSJ】开源 破解小牛电动车电池BMS(附源码) N1 N1S M1 U1 均可用

开篇: 之前发了一个贴子 破解小牛电动车电池BMS http://bbs.mydigit.cn/read.php?tid=2497489
这贴很水,只是简单了说一下破解的过程,但就这么一点点的贡献就得到了广大网友的支持。
这让我感到深深的惭愧。。。。让我久久不能入睡。。。。。。。。。。。。。`~~~~~~~~~~~ZZZ。。。
所以我考虑把代码发出来,造福大家,但又考虑到我自己的饭碗问题。
所以这次发一个阉割的版本。这次是干货,内容包括PCB源文件,与 IAR 源代码。
阉割的部分就是电容锁定在100%。其它的部分正常。
全系列车型可用,电路是V1.1版本的,最新的是V3.31,目前还没发布汗一个
电路部分是包括电压识别的,我只是在程序上阉割了,有能力的网友可以自己加上去这个功能

需要的工具:
STLINK 一个 刷程序用的
电路板:V1.1是之前剩下的,还有6个,有需要的可以问我(有图你都找不到我,那说明你应该是搞不定)
AD,IAR

哎,算了 TB 搜 【BYSSJ】可以找到我.
下图是正事。镇楼图

干货下载(电路图+IAR 源代码):NIU_BMS_V1.1_20180810_GNU

 

 

 

 

NIU_BMS_V1.11

STM8的ADC的五种工作模式

STM8的ADC的五种工作模式

STM8的ADC是10位的逐次比较型模拟数字转换器,多达16个多功能的输入通道。拥有5种转换模式,转换结束可产生中断。

STM8 ADC的初始化顺序如下:

1、AD输入通道对应的IO设置为上拉输入;

2、配置AD参数,如:预分频系数、是否使用外部触发转换、是否使用施密特触发器、是否使用缓存以及是否使用扫描模式等;

3、开启ADC;

4、开启转换;

注意!开启ADC和开启转换实际上都是置位ADON。

然后就可以通过ADC_DR寄存器读取转换后的值。

l单次模式:

在单次转换模式中,ADC仅在由ADC_CSR寄存器的CH[3:0]选定的通道上完成一次转换。该模式是在当CONT位为0时通过置位ADC_CR1寄存器的ADON位来启动的。

一旦转换完成,转换后的数据存储在ADC_DR寄存器中,EOC(转换结束)标志被置EOCIE 被置位将产生一个中断。

注意!初始化的时候只能选择一个通道。转换多个通道只能通过反复重新初始化或扫描模式!

示例程序:

void adc_init(void)

{

GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);//AIN4 IO设置为上拉输入

ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2);//预分频2

ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE);//不使用外部触发

ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE);

//禁止AIN2 AIN4的施密特触发器,降低IO静态功耗

//PD5,PD6上的通道如果施密特方式禁用会导致串口无法收发数据!

ADC1_ConversionConfig(ADC1_CONVERSIONMODE_SINGLE,//单次转换

ADC1_CHANNEL_4,//只能选择一个通道!

ADC1_ALIGN_RIGHT);//右对齐

ADC1_Cmd(ENABLE);//开启ADC

}

void main( void )

{

u16value=0;

adc_init();

while(1)

{

ADC1_StartConversion();//开启一次转换一次

while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC));//等待转换完成

ADC1_ClearFlag(ADC1_FLAG_EOC);//软件清除

value=(u16)ADC1_GetConversionValue();//从ADC_DR中读取ADC值

}

}

l连续模式与带缓存的连续模式:

在连换模式中,ADC在完成一次转换后就立刻开始下一次的转换。当CONT位被置位时即将ADC设为连续模式,该模式是通过置位 ADC_CR1寄存器的 ADON 位来启动的。

如果缓冲功能没有被使能(ADC_CR3寄存器的DBUF位=0),那么转换结果数据保存在ADC_DR寄存器中同时 EOC 标志被置位。如果EOCIE 位已被置位时将产生一次中断。然后开始下一次转换。

如果缓存功能被使能(DBUF=1),那么某个选定通道上的8个或者10个连续的转换结果会填满数据缓存(此时填满的是同一个通道的数据!!扫描模式时才是不同通道的数据!),当缓存被填满时,EOC(转换结束)标志被置位,如果EOCIE位已被置位,则会产生一个中断,然后一个新的转换自动开始。如果某个数据缓存寄存器在被读走之前被覆盖,OVR标志将置1。

如果要停止连续转换,可以复位清零CONT位来停止转换或者复位清零ADON位来关闭ADC的电源。

示例程序(不带缓存):

void adc_init(void)

{

GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);//AIN4 IO设置为上拉输入

ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2);//预分频2

ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE);//不使用外部触发

ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE);

//禁止AIN2 AIN4的施密特触发器,降低IO静态功耗

//PD5,PD6上的通道如果施密特方式禁用会导致串口无法收发数据!

ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换

ADC1_CHANNEL_4,//只能选择一个通道!

ADC1_ALIGN_RIGHT);//右对齐

ADC1_Cmd(ENABLE);//开启ADC

ADC1_StartConversion();//开启连续转换

}

void main( void )

{

u16value=0;

adc_init();

while(1)

{

while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC));//等待转换完成

ADC1_ClearFlag(ADC1_FLAG_EOC);//软件清除

value=(u16)ADC1_GetConversionValue();//从ADC_DR中读取ADC值

}

}

示例程序(带缓存):

void adc_init(void)

{

GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);//AIN4 IO设置为上拉输入

ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2);//预分频2

ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE);//不使用外部触发

ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE);

//禁止AIN2 AIN4的施密特触发器,降低IO静态功耗

//PD5,PD6上的通道如果施密特方式禁用会导致串口无法收发数据!

ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换

ADC1_CHANNEL_4,//只能选择一个通道!

ADC1_ALIGN_RIGHT);//右对齐

ADC1_Cmd(ENABLE);//开启ADC

ADC1_StartConversion();//开启连续转换

ADC1_DataBufferCmd(ENABLE);//开启缓存

}

void main( void )

{

u16value=0;

adc_init();

while(1)

{

while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC));//等待转换完成

ADC1_ClearFlag(ADC1_FLAG_EOC);//软件清除

value=0;

for(u8 i=0;i<10;i++)//

{

value+=ADC1_GetBufferValue(i);//将10个缓存中的值求和

}

value=value/10;//求出ADC的平均值

}

}

注意,只有在连续转换模式下ADC_DB寄存器才能称之为缓存,此时存储的是同一个通道多次转换的值。在单次扫描和连续扫描模式下该寄存器被用来存放不同通道的转换值。

l单次扫描模式:

该模式是用来转换从AIN0到AINn之间的一连串模拟通道,‘n’是在 ADC_CSR寄存器的CH[3:0]位中指定的通道编号(即CH[3:0]里配置第n个通道,就从通道0顺序递增逐个通道进行转换,直到第n个通道结束。例如,CH[3:0]里配置为AIN4,则对AIN0、AIN1、AIN2、AIN3、AIN4进行转换,其他通道不转换)。在扫描转换的过程中,序号 CH[3:0]位的值是被硬件自动更新的,它总保存当前正在被转换的通道编号。

单次转换模式可以在在SCAN 位被置位且CONT 位以被清零时通过置位 ADON 位来启动。

注意:当使用扫描模式时,不可以将AIN0到AINn之间通道对应的I/O口设为输出状态,因为ADC的多路选择器已经将这些I/O口的输出模块禁用了。

对于单次扫描模式,转换是从AIN0通道开始的,而且结果数据被存储在数据缓冲寄存器ADC_DBxR 中(例如,CH[3:0]里配置为AIN4,则ADC_DB0R存放AIN0的转换结果,ADC_DB1R存放AIN1的转换结果,以此类推。 ),当最后一个通道(通道‘n’)被转换完成后,EOC(转换结束)标志被置位,当EOCIE 位已被置位时将产生一个中断。

可以从缓冲寄存器中读取各个通道的转换结果值。如果某个数据缓存寄存器在被读走之前被覆盖,OVR标志将置1。

在转换序列正在进行过程中不要清零SCAN位;单次扫描模式可通过清零ADON位来立即停止。为了开启一次新SCAN扫描转换,可以通过对ADC_CR1寄存器的EOC位清零和ADON位置位来实现。

示例程序:

void adc_init(void)

{

GPIO_Init(GPIOC,GPIO_PIN_4,GPIO_MODE_IN_PU_NO_IT);//AIN2IO设置为上拉输入

GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);//AIN4 IO设置为上拉输入

ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2);//预分频2

ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE);//不使用外部触发

ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE);

//禁止AIN2 AIN4的施密特触发器,降低IO静态功耗

//PD5,PD6上的通道如果施密特方式禁用会导致串口无法收发数据!

ADC1_ConversionConfig(ADC1_CONVERSIONMODE_SINGLE,//单次转换

ADC1_CHANNEL_4,//配置通道号最大的那个

ADC1_ALIGN_RIGHT);//右对齐

ADC1_Cmd(ENABLE);//开启ADC

ADC1_ScanModeCmd(ENABLE);//开启扫描模式

}

void main( void )

{

u16value1=0;

u16value2=0;

adc_init();

while(1)

{

ADC1_StartConversion();//开启一次转换

while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC));//等待转换完成

ADC1_ClearFlag(ADC1_FLAG_EOC);//软件清除

value1=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL2)//读取AIN2的值

value2=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL4)//读取AIN4的值

}

}

l连续扫描模式:

该模式和单次扫描模式相近,只是每一次在最后通道转换完成时,一次新的从通道0到通道n扫

描转换会自动开始。如果某个数据缓存寄存器在被读走之前被覆盖,OVR标志将置1。连续扫描模式是在当SCAN位和CONT位已被置时,通过置位ADON位来启动的。在转换序列正在进行过程中不要清零SCAN位。

连续扫描模式可以通过清零ADON位来立即停止。另外一种选择就是当转换过程中清除CONT位那么转换会在下一次的最后一个通道转换完成时停止。

注意:在扫描模式(连续扫描模式)中,不要使用位操作指令(BRES)去清除EOC标志位,这是因为该指令是对整个ADC_CSR寄存器的一个读-修改-写操作。从CH[3:0]寄存器中读取当前的通道编号和写回该寄存器,将会改变扫描系列的最后通道编号。在连续扫描模式中正确的清除EOC标志位的方法是 个RAM变量中载入一个字节到ADC_CSR寄存器,这样来清除EOC标志位同时还重新载入扫描系列新的最后通道编号。

笔者实验发现,位操作指令只在连续扫描模式中会清除CH[3:0]寄存器中的值,但并不影响其他值。因此将ADC_CSR中的值读出,再将CH[3:0]中原来通道号加入进去,最后重新写入ADC_CSR中即可。写法如下:

ADC1->CSR = (uint8_t)(ADC1->CSR &(~ADC1_FLAG_EOC)|ADC1_CHANNEL_n);

注:ADC1_CHANNEL_n表示扫描到那个通道结束。

示例程序:

void adc_init(void)

{

GPIO_Init(GPIOC,GPIO_PIN_4,GPIO_MODE_IN_PU_NO_IT);//AIN2IO设置为上拉输入

GPIO_Init(GPIOD,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);//AIN4 IO设置为上拉输入

ADC1_PrescalerConfig(ADC1_PRESSEL_FCPU_D2);//预分频2

ADC1_ExternalTriggerConfig(ADC1_EXTTRIG_TIM,DISABLE);//不使用外部触发

ADC1_SchmittTriggerConfig(ADC1_SCHMITTTRIG_CHANNEL4,DISABLE);

//禁止AIN2 AIN4的施密特触发器,降低IO静态功耗

//PD5,PD6上的通道如果施密特方式禁用会导致串口无法收发数据!

ADC1_ConversionConfig(ADC1_CONVERSIONMODE_CONTINUOUS, //连续转换

ADC1_CHANNEL_4,//配置通道号最大的那个

ADC1_ALIGN_RIGHT);//右对齐

ADC1_Cmd(ENABLE);//开启ADC

ADC1_ScanModeCmd(ENABLE);//开启扫描模式

ADC1_StartConversion();//开启转换

}

void main( void )

{

u16value1=0;

u16value2=0;

adc_init();

while(1)

{

while(!ADC1_GetFlagStatus(ADC1_FLAG_EOC));//等待转换完成

ADC1->CSR = (uint8_t)(ADC1->CSR &(~ADC1_FLAG_EOC)|ADC1_CHANNEL_4);//软件清除

value1=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL2)//读取AIN2的值

value2=(u16)ADC1_GetBufferValue(ADC1_SCHMITTTRIG_CHANNEL4)//读取AIN4的值

}

}

至此,STM8的ADC的5种工作模式全部介绍完毕。总结一下学习经验就是仔细对照芯片手册编写程序,然后进行仿真调试,观察寄存器中的值的变化,从中领悟手册中的意思。