‘壹’ stm32的这句功能理解,但是C语言怎么解释 :RCC->CFGR|=1<<16
RCC->CFGR|=1<<16;相当于RCC->CFGR=(1<<16);或者r=1; r<<=16; RCC->CFGR=r;
而如果写成RCC->CFGR<<16|=1;这个是个语法错误的语句,原因是赋值号左边不能出现表达式。实际上你的字面意思是先将CFGR寄存器的值读出来然后左移16位再在最低位或个1,这不是将第16位置一的算法。
将第16位置1就是用这个寄存器或上一个第16位是1的数,1<<16就是这样一个数。
‘贰’ 使用TouchGFX开发STM32界面应用之入门篇(三)-- 多屏呈现与MVP框架(3)
【注:这是《TouchGFX入门篇(三)-- 多屏呈现与MVP框架》文章的第二部分的续篇,之前的部分请 看这里 】
之前我们已经完成了2个Screen的创建,以及他们之间的相互切换和数据交换。下面继续实现时钟的走时和圆弧的动画。
为了让时间显示不断变化,一个通常的想法是:利用STM32的定时器。而在touchgfx中,实现这个功能变得更加简单:touchgfx直接提供了一个“滴答”函数(以及自动运行这个功能的机制)---- handleTickEvent()。限于文章篇幅,关于这个函数的具体解释请见touchgfx的API说明手册,这里直接展示如何应用他:
上图中在Screen2View.hpp中添加了 handleTickEvent() 函数声明和一个变量 tickCount (记录tick的数值,逢60进位)。然后在Screen2View.cpp中实现handleTickEvent() :
模拟器运行效果如下:
还需要最后一步:处理圆弧,让他动起来!圆弧动画的原理:定时更新圆弧的起始位置和结束位置。增加2个变量addStart、addEnd,分别控制首末位置的“增量”,addStart==2、addEnd==1时,起始位置变得快、结束位置变得慢,看起来就是圆弧长度在缩短;反之,圆弧长度则不断变长(不理解这个算法没关系,看下面图示):
细心的你可能已经看到:设置页面里设置的是分钟,但运行页面里分钟却是按秒变化的 ---- 这个bug留给你修改吧。
最后,烧写到目标板上跑跑看!(本讲完毕)
‘叁’ 如何stm32解决烧写地址算法重叠
1、 打开J_Flash ARM
出现如下工作界面:
2、选择Options—Project settings
进入Project settings界面:,
3、点击CPU,选择合适的型号:
此时,我们在device里面选择好我们目标板上的芯片就可以了,然后点击确认。
4、我们现在将JLINK和目标板进行连接:Target ——connect
没有跳出错误,说明连接成功。
5、打开我们要烧录的hex文件:
选择需要烧录的程序:
选择HEX文件后如下图所示:
7.烧录程序:
如果有弹出窗口,选择是或者确定。
8.重启设备
‘肆’ 最近学习STM32,发现一个奇怪的问题,32位的系统,存储器地址空间应该是2的32次方bit,怎么计算,都是4Gbit
你的算法没问题,你的理解有问题!32位的操作系统理论上最多支持4GBytes是没问题的。
1Bytes(字节)=8bits(字位)也是没问题的。你存在概念性错误,你的计算方案应该是以CUP的32位数来计算就只有2^32bits= 512MByte,然而2^32Byte=4GB。
首先你要区分32位操作系统和32位的处理器(cpu)的区别和联系。
(一)32位CPU每次可以处理32个字位,即32bits=4Bytes。每一个字节都有一个地址,其中包含了8个字位。32位CPU有32跟地址线,地址线决定了寻址范围的能力。每一根线都决定0和1两个地址,那么两根线就会决定00,01,10,11这四个地址,依次类推,32根线总共会决定出2^32次方个位置。这样计算是没有问题的。每一个位置都是1Byte,这是内存的基本单位,所以32位操作系统配32位CPU,理论上可以寻找4GB的地址。由于硬件等原因,系统显示会不到4G。
(二)32位操作系统与64位CPU的搭配。64位CPU有64根地址总线,理论支持2^32/1024^3 G的内存,这是一个很大的数字,但是实际上我们市场的电脑目前远远达不到这个理论内存的万分之一。目前64位也就是能支持120G的内存。但是如果64位CPU和32位操作系统搭配,也仅仅会支持4G的内存。因为在32位系统下,仅有32根地址线工作。
(三)寻址范围是由地址线个数确定的,而不是CPU位数确定的。CPU的地址总线根数决定了能找到多少个字节(Byte),数据总线的宽度决定了能够一次传送多少个二进制位(bit) 。希望你能明白其中的关系,不要把地址线直接和字位(bit)联系在一起。
‘伍’ STM32的加密库在哪儿
关于STM32加密
摘要:
知识产权的保护,如何让自已辛勤的劳动成果不被别人抄袭,采用有效的手段对IC加密是值得每一个设计者关注的问题。
当然,有人说,没有解不了密的IC,的确,解密是一项技术,只要有人类在不断的研究,它就有破解的一天;但是加密后的IC会增加破解的难度与破解成本,当破解的成本大于收益时,自然就会使破解者望而却步。
STM32芯片这两年销量很好,它的性能和价格都很不错,但如何对STM32进行加密呢,本人结合自已使用STM32 MCU一年多的经验,总结一下我对它加密的理解与方法。
关键字: STM32 加密 读保护
加密,最基本的方法是置读保护,这样可以防止外部工具非法访问,在STM32官网发布的 串口ISP软件中有置读保护和加密选项,选择一个就可以了,这样外部工具就无法对FLASH进行读写操作,但我要重新烧写FLASH怎么办?只能清读保护,而清读保护后,芯片内部会自动擦除FLASH全部内容。
还有人说,置读保护还不够安全,说要采用芯片内的唯一ID来加密,在程序里识别芯片的ID,如果ID不对,则程序不运行,当然,这样安全性又要更高一些,但每个芯片的ID不一样,因此对应的程序也应该不一样,那如何处理呢?有网友说:采购的时候,产品同批生产的ID号应该是连续的,可以通过判别ID的范围;还有网友说,在烧录工具里做一个算法,读取芯片ID,再修改相应的二进制文件。当然还会有很多种方法,这里不展开讨论。
以上介绍的只是一种情况,在实际的应用中还会发生第二种情况。
我们知道,STM32的内部FLASH是用户可编程的,也就是说它支持IAP,而IAP中的APP代码一般是需要开放的,那么只有保证BOOT的代码安全,才能确保不被破解。
前面提到,当IC置读保护后,外部工具不能访问内部FLASH,但CPU可以访问,破解者完全可以自已编写一段代码通过BOOT下载到IC 运行,然后在程序中读出你的BOOT代码。
只能加以限制,使别人的代码运行不了,才能保证BOOT不被读出。
常用的方法是采用加密算法,如AES;流程如下:
APP代码加密,下载时,在BOOT中解密,这样,只有通过正确加密的APP代码才能正常的运行,因此加密的算法就成了你的密钥,而这个是你独有的。
参考资料:《STM32F1x Flash Programming.pdf》作者:STMicroelectronics
‘陆’ 求生成MD5码的c或c++代码
自己用C语言写的简单的MD5算法实现。
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
typedef unsigned char BYTE;
typedef unsigned int UINT;
typedef UINT MD5_SUB_ARRAY[16];
typedef UINT MD5_TRANSORM_FUNC(UINT,UINT,UINT);
typedef struct
{
UINT a;
UINT b;
UINT c;
UINT d;
MD5_SUB_ARRAY sub_array;
}MD5_TRANSFORM_PARAM;
const double MAX_INT = (double)0xFFFFFFFF + 1.0;
const UINT MD5_TRANSFORM_MATRIX[4][16][3] =
{
{
{ 0, 7, 1}, { 1,12, 2}, { 2,17, 3}, { 3,22, 4},
{ 4, 7, 5}, { 5,12, 6}, { 6,17, 7}, { 7,22, 8},
{ 8, 7, 9}, { 9,12,10}, {10,17,11}, {11,22,12},
{12, 7,13}, {13,12,14}, {14,17,15}, {15,22,16},
},
{
{ 1, 5,17}, { 6, 9,18}, {11,14,19}, { 0,20,20},
{ 5, 5,21}, {10, 9,22}, {15,14,23}, { 4,20,24},
{ 9, 5,25}, {14, 9,26}, { 3,14,27}, { 8,20,28},
{13, 5,29}, { 2, 9,30}, { 7,14,31}, {12,20,32},
},
{
{5, 4, 33}, { 8,11,34}, {11,16,35},{14, 23,36},
{1, 4, 37}, { 4,11,38}, { 7,16,39},{10, 23,40},
{13,4, 41}, { 0,11,42}, { 3,16,43},{ 6, 23,44},
{9, 4, 45}, {12,11,46}, {15,16,47},{ 2, 23,48},
},
{
{ 0,6,49}, { 7,10,50}, {14,15,51},{ 5, 21,52},
{12,6,53}, { 3,10,54}, {10,15,55},{ 1, 21,56},
{ 8,6,57}, {15,10,58}, { 6,15,59},{13, 21,60},
{ 4,6,61}, {11,10,62}, { 2,15,63},{ 9, 21,64},
},
};
static UINT MD5_TRANSFORM_ARRAY[65];
void MD5_Init()
{
int x;
for(x = 1; x <= 64; x++)
{
MD5_TRANSFORM_ARRAY[x] = (UINT)(MAX_INT * fabs(sin(x)));
}
}
UINT F(UINT x,UINT y,UINT z)
{
return ((x & y) | ((~x) & z));
}
UINT G(UINT x,UINT y,UINT z)
{
return ((x & z) | (y & (~z)));
}
UINT H(UINT x,UINT y,UINT z)
{
return (x ^ y ^ z);
}
UINT I(UINT x,UINT y,UINT z)
{
return (y ^ (x | (~z)));
}
BYTE* MD5_prepare_data(const BYTE* data,int len,int* new_len)
{
int rest,fill,size;
BYTE* new_data;
UINT bit_len;
// (1) 字节补齐
rest = len % 56;
if (rest <= 56) fill = 56 - rest;
else fill = (64 - rest) + 56;
new_data = (BYTE*)malloc(len + fill + 8);
if (NULL == new_data) return NULL;
if (len > 0) memcpy(new_data,data,len);
if (fill > 0) memset(new_data + len,0x80,1);
if (fill > 1) memset(new_data + len + 1,0,fill - 1);
size = fill + len;
// (2) 附加数据的比特长度
bit_len = len * 8;
// (64位二进制数表示的)比特长度的低32位
memset(new_data + size + 0,(bit_len & 0x000000FF), 1);
memset(new_data + size + 1,(bit_len & 0x0000FF00) >> 8, 1);
memset(new_data + size + 2,(bit_len & 0x00FF0000) >> 16,1);
memset(new_data + size + 3,(bit_len & 0xFF000000) >> 24,1);
// 不考虑比特长度超出32位无符号数表示范围,所以高32位总是0
memset(new_data + size + 4,0,4);
*new_len = size + 8;
return new_data;
}
void MD5_transform(MD5_TRANSFORM_PARAM* param,int ring,MD5_TRANSORM_FUNC func)
{
UINT a,b,c,d,s,k,i;
UINT abcd[4];
UINT *X,*T;
int index;
abcd[0] = param->a;
abcd[1] = param->b;
abcd[2] = param->c;
abcd[3] = param->d;
X = param->sub_array;
T = MD5_TRANSFORM_ARRAY;
for(index = 0; index < 16; index++)
{
a = abcd[(3 * index + 0) % 4];
b = abcd[(3 * index + 1) % 4];
c = abcd[(3 * index + 2) % 4];
d = abcd[(3 * index + 3) % 4];
k = MD5_TRANSFORM_MATRIX[ring][index][0];
s = MD5_TRANSFORM_MATRIX[ring][index][1];
i = MD5_TRANSFORM_MATRIX[ring][index][2];
a = a + func(b,c,d) + X[k] + T[i];
a = ( a << s) | ( a >> (32 - s)); // 循环左移
a = a + b;
abcd[(3 * index + 0) % 4] = a;
}
param->a = abcd[0];
param->b = abcd[1];
param->c = abcd[2];
param->d = abcd[3];
}
int MD5(const BYTE* data,int len)
{
int x,y,new_len;
MD5_TRANSFORM_PARAM param;
UINT AA,BB,CC,DD;
BYTE* buf;
MD5_Init();
buf = MD5_prepare_data(data,len,&new_len);
if (buf == NULL) return -1;
AA = 0x67452301;
BB = 0xefcdab89;
CC = 0x98badcfe;
DD = 0x10325476;
for(x = 0; x < new_len / 64; x++)
{
param.a = AA;
param.b = BB;
param.c = CC;
param.d = DD;
for(y = 0; y < 16; y++)
{
param.sub_array[y] = buf[64 * x + 4 * y + 0];
param.sub_array[y] += buf[64 * x + 4 * y + 1] << 8;
param.sub_array[y] += buf[64 * x + 4 * y + 2] << 16;
param.sub_array[y] += buf[64 * x + 4 * y + 3] << 24;
}
MD5_transform(¶m,0,F);
MD5_transform(¶m,1,G);
MD5_transform(¶m,2,H);
MD5_transform(¶m,3,I);
AA += param.a;
BB += param.b;
CC += param.c;
DD += param.d;
}
printf("MD5(\"%s\")=",data);
printf("%02X%02X%02X%02X",
(AA & 0x000000FF),
(AA & 0x0000FF00) >> 8,
(AA & 0x00FF0000) >> 16,
(AA & 0xFF000000) >> 24);
printf("%02X%02X%02X%02X",
(BB & 0x000000FF),
(BB & 0x0000FF00) >> 8,
(BB & 0x00FF0000) >> 16,
(BB & 0xFF000000) >> 24);
printf("%02X%02X%02X%02X",
(CC & 0x000000FF),
(CC & 0x0000FF00) >> 8,
(CC & 0x00FF0000) >> 16,
(CC & 0xFF000000) >> 24);
printf("%02X%02X%02X%02X",
(DD & 0x000000FF),
(DD & 0x0000FF00) >> 8,
(DD & 0x00FF0000) >> 16,
(DD & 0xFF000000) >> 24);
printf("\n");
return 0;
}
int main()
{
MD5("",0);
MD5("a",1);
MD5("abc",3);
MD5("message digest",14);
MD5("abcdefghijklmnopqrstuvwxyz",26);
return 0;
}
‘柒’ stm32单片机能写复杂控制算法么
1.PID原理
1.1 P I D三个参数简单理解
1.2 P I D
1.3 PI PD PID适用系统
2.串级PID原理
3.PID代码
3.1 单级PID
3.1.1 初始化PID结构体
3.1.2 单级PID计算
3.1.3PID初始化
3.1.4 清空PID
3.2 串级PID
3.2.1 初始化串级PID结构体
3.2.2 串级PID计算
4.PID的使用
4.1 定义PID结构体并初始化
4.2 定义电机速度函数
4.3 在检测霍尔码盘时发送速度给电机
4.4 实验效果
1.PID原理
PID是什么,P,I,D的分别功能
你和PID调参大神之间,就差这篇文章!
1.1 P I D三个参数简单理解
P(比例): 简单来说,P就是凉了加热水,热了加凉水。比目标值小,我就增加一点,比目标值大,我就减小一点。(现在)
P可能出现的问题: 1.P太小,达到目标值需要花费很长的时间,而且会有稳态误差。2.P太大,达到目标值时可能会一直震荡。
I(积分): 将一段时间内的误差累积起来加到输出上,可以消除历史误差对当前实际曲线的影响,提高系统的稳定性。 (过去)
I可能出现的问题: 1.I太小,可以消除稳态误差,但太慢了,对于某些需要很快响应的系统,显然不能满足要求。2.I太大,累计误差占比过大,就会出现抖动现象,难以收敛。
D(微分): 减小最大超调量。(下图中③就是最大超调量。) 可以有效减小震动的幅度。让曲线收敛更快 (未来)
D可能出现的问题: 1.D太小,作用小,时间长。2.D太大,为了减小超调量,补偿的过多,导致震荡很久。
在这里插入图片描述
1.2 P I D
先调P,逐渐增加P直到系统出现震荡,将当前值乘0.7就是较为合适的值。
再调I,将稳态误差逐渐降低。
后调D,将最大超调量降到最低。
1.3 PI PD PID适用系统
PI:响应速度要求不那么高的系统。
PD:大惯性系统。超调量太大。
PID:都可以。
网上将PID原理太多太多了,我的理解也都是参见上面的内容。认真看肯定有收获。
2.串级PID原理
【串级PID】浅谈串级PID作用及意义——快速理解串级PID结构优势
这里个人理解就是,单机PID就是稳定速度。而需要带位置和角度的就要用串级PID了。常用于平衡车,板球系统等。
而转速闭环称为串级PID的内环,位置 (角度) 闭环称为串级PID的外环。其实也很好理解,位移是速度的积分,只有速度慢慢稳定,位置才能确定。
3.PID代码
3.1 单级PID
3.1.1 初始化PID结构体
typedef struct _PID
{
float kp,ki,kd;
float error,lastError;//误差、上次误差
float integral,maxIntegral;//积分、积分限幅
float output,maxOutput;//输出、输出限幅
}PID;
1
2
3
4
5
6
7
1
2
3
4
5
6
7
3.1.2 单级PID计算
#define LIMIT(x,min,max) (x)=(((x)<=(min))?(min):(((x)>=(max))?(max):(x)))
//单级pid计算
void PID_SingleCalc(PID *pid,float reference,float feedback)
{
//更新数据
pid->lastError=pid->error;
pid->error=reference-feedback;
//计算微分
pid->output=(pid->error-pid->lastError)*pid->kd;
//计算比例
pid->output+=pid->error*pid->kp;
//计算积分
pid->integral+=pid->error*pid->ki;
LIMIT(pid->integral,-pid->maxIntegral,pid->maxIntegral);//积分限幅
pid->output+=pid->integral;
//输出限幅
LIMIT(pid->output,-pid->maxOutput,pid->maxOutput);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3.1.3PID初始化
void PID_Init(PID *pid,float p,float i,float d,float maxI,float maxOut)
{
pid->kp=p;
pid->ki=i;
pid->kd=d;
pid->maxIntegral=maxI;
pid->maxOutput=maxOut;
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
3.1.4 清空PID
//清空一个pid的历史数据
void PID_Clear(PID *pid)
{
pid->error=0;
pid->lastError=0;
pid->integral=0;
pid->output=0;
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
3.2 串级PID
3.2.1 初始化串级PID结构体
typedef struct _CascadePID
{
PID inner;//内环
PID outer;//外环
float output;//串级输出,等于inner.output
}CascadePID;
1
2
3
4
5
6
1
2
3
4
5
6
3.2.2 串级PID计算
//串级pid计算
void PID_CascadeCalc(CascadePID *pid,float angleRef,float angleFdb,float speedFdb)
{
PID_SingleCalc(&pid->outer,angleRef,angleFdb);//计算外环(角度环)
PID_SingleCalc(&pid->inner,pid->outer.output,speedFdb);//计算内环(速度环)
pid->output=pid->inner.output;
}
1
2
3
4
5
6
7
1
2
3
4
5
6
7
4.PID的使用
STM32应用(九)编码器及其测速原理、L298N电机驱动控制编码器电机
在这篇博客的配置下,只需要修改部分代码。以单级PID为例子。
4.1 定义PID结构体并初始化
PID pid;
void Motor_Init(void)
{
PID_Init(&pid,10,0,0,1000,1000);
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim1,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
HAL_TIM_Base_Start_IT(&htim6); //开启10ms定时器中断
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //开启PWM
__HAL_TIM_SET_COUNTER(&htim1, 10000); //编码器定时器初始值设定为10000
motor.loopNum = 0; //防溢出
}
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
4.2 定义电机速度函数
void Motor_Send()
{
float output = 0;
PID_SingleCalc(&pid, motor.targetSpeed, motor.speed);
output = pid.output;
if(output > 0) //正转
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, (uint32_t)output);
IN1(1);
IN2(0);
}
else //反转
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, (uint32_t)(-output));
IN1(0);
IN2(1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4.3 在检测霍尔码盘时发送速度给电机
if(htim->Instance==htim6.Instance) //10ms中断
{
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
motor.totalAngle = pluse + motor.loopNum * RELOADVALUE/2;
motor.speed = (float)(motor.totalAngle - motor.lastAngle)/(4*13*RR)*6000; //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
motor.lastAngle = motor.totalAngle; //更新转过的圈数
Motor_Send();//发送速度
}
‘捌’ 怎样对STM32加密,防止被读出复制
1,如果板子上有外部存储器,可以先编写一个程序,利用算法把id计算得到一些值存入外部存储器,然后再烧写真正的程序,真正的程序去校验外部存储器的数据是否合法即可
2,利用板子上按键组合,或是上电按住某些键,程序在这个时候利用算法把id计算得到一些值存入程序区(stm8为EE区),程序运行时去验证程序区数据是否正确
3,轩微编程器有软件加密的功能,编程器会读芯片id,根据算法直接改写缓冲区,达到软件加密的作用
4,读出的id通过一定算法,例如异或加上一个数,得到的数据存入flash(只运行一次,运行后标志位也存入flash),下次读到这个标志位,就不运行这个程序。
四、做软件加密时注意
1,不要在程序中直接出现id地址,例如STM32:1FFFF7E8 1FFFF7EC 1FFFF7F0 STM8: 0x4865~0x4870
2, 利用校验和或是crc对程序区进行校验,防止改程序
‘玖’ stm32求电流有效值算法
电流的有效值应该从电流产生的热效应来考虑,总的热是 I^2*Rt,,R是电阻,t是产生这么多的热所用的时间,公式里面的I 就是电流的有效值