单片机零碎知识

1.stm32的union中的数据为大端模式

     大端模式:便于阅读

    小端模式:便于存储

.....
union T{
     u16 ch16;
     u8  ch8;
}
union T t;
t.ch16=1234;
.....
log(t.ch8[0]);//34  0x2000CBD0 低地址 高位数字
log(t.ch8[1]);//12  0X2000CBD1 高地址 低位数字
......

2.指针没有分配内存时,不能进行strcat和strncpy等操作,为了节省内存,可以动态malloc分配,而静态数组分配太过占据内存的情况。

.....
char *p,temp;
char ne[10]={0};
char cp[10]="1234569789";
temp=&cp[2];
strncpy(p,temp,3);
strcat(ne,p);//输出结果为不可控
//可以先为p分配内存
p=malloc(4);
strncpy(p,temp,3);
strcat(ne,p);//就会ne=“345”
free(p);//释放内存,节省堆空间,就是启动文件中的heap大小
p=NULL;//此处是防止p成为悬空指针

3.ITM调试打印输出

    要求:内核支持ITM,硬件需要J-link的swo与PB3连接。时钟何数据正常连接。swo就是你打印输出的数据。DEBUG中需要在jlink中开启Trace功能。配置代码:

.....
//ITM:指令跟踪宏单元
//printf函数重新绑定输出
int fputc(int ch,File *f)
{
ITM_Send_Char(ch);//此函数默认绑定ITM 的port0
return 0;
}
......
//一般我们都用串口直接打印,ITM需要硬件支持和支持的烧录外设,如jlink,stlink。

debug时打开View-》serial_window->debug(printf)view窗口就可以看到数据输出了。

4.函数指针

...
int function(char *ch)
{

return 1;
}
.....
typedef int (*function_types)(char *argc);//声明指针类型

function_types function_point;//定义一个函数指针
function_point=function;//绑定函数
function_point();//调用函数
......
可以更好的规划程序框架,高内聚,低耦合

5,字符串转与数值互转

.....
sscanf(str,%d,&num);//将字符串str转为数值存放到num
....
sprintf(str,%d,num);//将数值转成字符串存放到str
....
//直接转
char str[1]="5";
int num=(int)str[0]-'0';
//标准库
#include《stdlib.h》
char str[]="526";
int num=atoi(str);
....
//数子转字符串
int y=1456;
str[0]=y/1000+'0';
y=y%1000;
str[1]=y/100+'0';
y=y%100;
str[2]=y/10+'0';
y=y%10;
str[3]=y+'0';
//先要判断数字的位数,可以用范围法,但是大于u64就比较复杂了,需要自己去拼凑更大数值

6.搞底层的,可能会经常遇到拼接字符串的情况,现在我们可以利用这个库函数来实现;

#include "stdio.h"

#define maxlength 128
#define Debug 1
int main(int argc,char *argv[]){
char cmd[]="AT+CIPSTART=%d,TCP,%s,%d\r\n";//连接到服务器指令

//此条指令可以发现很多数据没有填充,那么怎么填充呢?
int link_id=1;
char ip[]="www.dshuibi.com";
int port=666;
char post_cmd[maxlength];
snprintf(post_cmd,sizeof(post_cmd),cmd,link_id,ip,port);
//注意,snprintf(转化后放的,转化后的大小,格式字符串,替换字符,替换字符);
#if Debug
printf("%s",post_cmd);//AT+CIPSTART=1,TCP,www.dshuibi.com,666
#endif
//这样就拼凑成功了,当然也可以自己写函数
return 0;
}
//那么反过来是如何操作的,从字符串取值呢?
char fmt[]=“recived:%d,%d:\r\n”;
char data[]="recived:10,28:nice to meet you,my frien!\r\n";
int type,length;
sscanf(data,fmt,&type,&length);//type=10,length=28
完成字符串特定格式数据的提取。

7.地址转指针再转函数

//此代码stm32和GD32都可以
#define Start_addar  0x0800 0000

typedef void(*function_pointer)(void);
.....
function_pointer  fun_pointer;
fun——pointer=(function_pointer)*(u32*)Start_addar;//先将地址转成地址指针,再解指针取值,再将值转成函数指针。
fun_pointer();//调用函数
.......
/*在单片机裸机中,想搞定地址变程序跳转,需要注意这几点*/
1.主堆栈指针,一般是MSP,他的值就是Start_addar;
2.跳转函数,它的值一般是Start_addar+4
3.屏蔽所有中断
4.在跳转过去的程序中开启中断,向量表重映射
//boot
...
__set_PRIMASK(1);//屏蔽所有中断
 if(((*(vu32*)APP_Address)&0x2FFE0000)==0x20000000)	//检查栈顶地址是否合法.
	{ 
        jump2app=(IAP_Fun)*(vu32*)(APP_Address+4);		//用户代码区第二个字为程序开始地址(复位地址)
        __set_MSP(*(vu32*)APP_Address);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	}
....
//跳转过去程序
....
__set_PRIMASK(0);
SCB->VTOR=Start_addar;//向量表重映射
...

可以发现,一个程序它在flash中的地址是0x0800 0000+offset,它必然要被加载到0x2000 0000,这个区域就是程序堆栈区,*(vu*)Start_addar & 0x2ffe 0000=0x2000 0000。0x0800 0000存储的是主栈(MSP)地址,0x0800 0004存储的是程序起始地址。

学过51的都知道,程序的执行就是在几个寄存器(sp.pc,r0~r7)中不停的循环。那么这个主栈MSP就是(sp,pc,r0~r15),这几个寄存器的位置和值的声明,然后pc+1,程序就从起始地址开始跑起来了。

8.主栈msp的值必然是0x2000 0000,那么在freertos中呢?

     freertos中每个程序都有自己的栈,一般一个数组定义它。它是从msp转成sp的。

9.关于数组初始化的问题:

      全局数组,如果没有初始化,那么他的值为0,但是如果是非静态的局部数组或者函数内部的数组,那么他的值就是随机的。

数组初始化的方法;
int array[10]={0};
memset(array,0,sizeof(array));
char str[100]="abcdef";
char *strp="acdef";
注意:
   0在字符数组的含义比较特殊,字符串处理函数检测到0默认结束。这个0是 char c=0;而不是 char c1='0';
  char p='0';//p=48
  char  p=0;
比如可见字符范围:0x20~0x7e  空格sp和~

10.互斥的概念

int mutxe;
mutxe=0;
char get_mutxe()
{
  if(mutxe!=1)
    {
     mutxe=1;
     return 1;
   }
else
  return 0;
}
void free_mutxe()
{
  mutxe=0;
}

void task1()
{
  if(1!=get_mutxe())//获得锁
      return;
   ............
  free_mutxe();//释放锁

}
void task2()
{
  if(1!=get_mutxe())
      return;
   ............
  free_mutxe(); 
}
void main()
{
 task1;
}
void isr()
{
task2;
}

在freertos中,获得锁会行为阻碍其它更高优先级的任务阻塞,直到别的任务释放锁它才能继续运行。本质是将低优先级拉到和高优先级同一个优先级。

11.一些很容易告糊涂的程序;

switch(step){
case 1:
   printf("这是老1");
case 2:
   printf("这是老2");
case 3:
     break;
case 4:
   printf("这是老4");
case 5:
   break;
}
step=1时输出:
  这是老1
  这是老2
step=4时输出:
  printf("这是老4");
step=3时没有输出
  

switch(step)
{
  case 1: do {step++;
  case 2:step++;
  case 3:step++;
  case 4:step++;
  case 5:}while(step<10);
}
step=3时调用此函数输出什么?

12,stm32中的void *

    它叫万能指针,可以转换成任何类型的指针,一般在不明确调用者的类型时。比如:

     void * pobject;

   //object可能是个基本类型,也可能是个结构体。甚至是个复杂结构体。

13.宏定义判断与预定义判断

    

//一定要分清楚#ifdef 和#if的区别
#define hong_x  0




#ifdef hong_x   //这个宏有没有定义,只要说了就是定义了,不管它的值是不是0
printf("宏已经定义");
#else
printf("宏未定义")
#endif


#if hong_x     //判断结果为真才会执行,0的结果为假
 printf("结果为真")
#else
 printf("结果为假")
#endif

输出结果:
宏已经定义
结果为假

//文件防止重复加载

#ifndef  _xxx_h
#define  _xxx_h
....
#endif

14,预编译排错

#include <assert.h>


void assert(int expression);//如果表达式为0,编译器会输出当前位置,终止程序运行。非0程序继续执行。非常好用,不确定的队列,链表,计算,类型等判断都可以使用


#error 直接让编译输出


#if nodebug
#error "This no debug"
#else
.......
#endif


//查错和开发时常用,但是在正式版本一般关闭,会消耗额外的资源和数据

15.三角函数的使用

#include "math.h"
#include "stdio.h"

void main(void){
  double a,b,c=5;
  double pai=3.1415926;
  double angle=30;
  a=c*sin(30*pai/180);
  b=c*cos(30*pai/180);
  printf("a=%d;b=%d",a,b);
}


//一定要把角度换算成弧度

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值