C语言和单片机学习中零散的知识点

1.关于计算打印字符。
int a = 10;
int b =3;
int c = a / b;
printf("%03d,c")   //这里输出的是003   如果是%02d,输出的是03            这里的%03d表示输出
printf("%3d\r\n", c);    //如果前面没有两个03的话,会打印  3。前面是两个空格。

printf("20%06d",c);    //输出20000003    前面加一个20表示加上了一个固定的字符串20

float d = a / b;
printf("%f", d) ;     //3.000000         得到3.000000是因为a和b都是整数,所以会输出小数点后面没有数字
printf("20%f", d);    //203.000000
printf("%02f", d);    //模仿d的写法,结果仍然是3.000000  不会修改个数的。

float d = 10.0;
int e = 3;

float f = d / e;
printf("%f", f);                     //3.33333305.1
printf("%05.1f\r\n", f);       //打印003.3    这里的意思是一共有5位数,不够的前面补零。.1表示后面跟着1位小数
printf("%5.1f\r\n", f);         //打印     3.3 这里相当于把0替换成了空格。
printf("%05.2f\r\n", f);     //打印03.33

2.\r\n

\r:将当前位置移到本行的开头,又叫回车。

\n:将当前位置移到下一行开头,又叫换行。

不加\r\n

都加上\r\n

只加\n

只加\r

3.如下图所示,在C语言中,定时String类型的数据会报错

C语言中定义不了String,报错未定义的标识符String。

这个S大写和小写都不行

加上了头文件<String.h>也不行

用C++试一下

后来查资料可知,C语言中是没有定义没有定义字符串类型的。

如果想要表示字符串类型的,C语言中是靠char[20] 数组来实现的。

4.UART和USART的区别

UART是通用异步收/发器

USART是通用同步/异步收/发器

在异步通信中,发送方和接收方之间没有公共时钟。它广泛用于面向字节的传输,这意味着它一次可以发送 1 个字节或字符。与同步相比,异步通信速度较慢,并且还具有启动和停止位的开销。在异步通信中,每一帧都用开始和停止位进行包装。让我们看看异步通信的帧结构。

 在同步通信中,时钟由发送方和接收方共享。我们可以在其中传输一个数据块,并且对于该数据块只有一个开始和停止位。这是同步通信开销较小的原因。

总的来说就是异步通信通信的双方没有相同的时钟线控制,波特率等一些参数需要自己设置,同步通信有一根时钟线控制。

int sprintf(char* buffer,const char *format[,argument]……);   格式化字符串

把format里面的东西复制到buffer里面,

第5.一个参数是转换后要存的地方,第二个参数是格式化字符串,比如%d,%f,%c,

注意:这里要用sprintf_s 而不是sprintf  不然会报错。

5.在使用LVGL的时候,如果屏幕出现白屏,可以将启动文件中的Stack_Size  EQU   0x00000400 

改成0x0000080

如果出现内存不足的情况,可以将点击Options–>Target中Rom和Ram的值修改了。或者将malloc文件下的MEM1_MAX_SIZE改小一点。

6.C语言中char和char数组输出字符串

 

这里可以看到,char类型给的数组长度是20,后面空格自动省略了,因为我后面直接输出字符a时是紧跟在char数组输出内容的后面。

  1. char str[7];
  2. str = "abc123"; //错误
  3. //正确
  4. str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
  5. str[3] = '1'; str[4] = '2'; str[5] = '3';

这里要注意有一个坑,字符数组只有在定义时才能将整个字符串一次性的赋值给它,一旦定义完了,就只能一个字符一个字符的赋值了。

要注意的是,在每次给字符数组赋值的时候,要在结尾加上'\0'.

如果不加\0

加上\0之后

如果是直接定义则不需要加\0,加和不加结构都是一样的。

可以用string.h文件中的strlen函数求数组的长度

注意\0是不被计算到总长度里面的。

从下图可以看到,如果字符型的单个赋值的话,如果尾部不加'\0',不仅打印的数据会出错,通过strlen计算得到的长度也会报错。

7.C语言中的结构体

 

结构体struct 后面的Student是标签,在main函数中使用的时候 要先Student student2,然后

student.name 这样才可以赋值.

而结构体后面跟着的studnet1,在main函数里可以直接student.name的方式赋值。

如果前面跟了typedef关键字,那么student1就等同于Student了。在面函数需要先定义才可以。

此外,还可以在结构体这里直接赋值

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};

用(.)的方式可以访问结构体成员。

结构体也可以作为函数的参数。

结构体指针

使用结构体指针不能直接赋值,如下图所示,这样的方式是错误的。

#include <stdio.h>

struct Student
{
    int age;

    int num;
};

int main()
{
    Student abc;
    Student *student;
    student->age = 10;
    printf("%d", student->age);

    return 0;
}

结构体指针不能写成和普通结构体那样的同样的赋值的方法,二者不是一样的。

结构体指针应该这样赋值,结构体指针用 -> 取值。

#include <stdio.h>

struct Student
{
    int age;

    int num;
};

int main()
{
    Student abc;
    struct Student* student = &abc;
    student->age = 10;
    printf("%d", student->age);

    return 0;
}

从上图可知,如果用tpedef定义一个结构体的话,后面,必须要加struct,struct后面可加可不加,具体看情况,如果typedef enum 是这种情况的话,在mian函数下面直接定义给值就可以了。

8.C语言之指针

 #include <stdio.h>

int main()
{
    int a = 5;
    printf("a = %d\r\n", a);
    printf("&a = %d\r\n", &a);

    int* p;
    p = &a;
    printf("p = %d\r\n", p);
    printf("*p = %d\r\n", *p);
    printf("&p = %d\r\n", &p);

    int** q;
    q = &p;
    printf("q = %d\r\n", q);
    printf("*q = %d\r\n", *q);
    printf("&q = %d\r\n", &q);

    int** m;
    m = q;
    m = &q;    //报错
    m = &a;    //报错

    return 0;
}

9.C语言中一些函数的使用

memcpy

C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1

下面是 memcpy() 函数的声明。

void *memcpy(void *str1, const void *str2, size_t n)

参数

  • str1 — 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
  • str2 — 指向要复制的数据源,类型强制转换为 void* 指针。
  • n — 要被复制的字节数
  • 返回值

    该函数返回一个指向目标存储区 str1 的指针。

    // 将字符串复制到数组 dest 中
    #include <stdio.h>
    #include <string.h>

    int main()
    {
        const char src[50] = "http://www.runoob.com";
        char dest[50];

        //printf("%d\r\n",strlen(src));

        memcpy(dest, src, strlen(src)+1);
        printf("dest = %s\n", dest);

        return(0);
    }

    结果

    Strlen 计算字符串长度

    这里  memcpy(dest, src, strlen(src)+1) 要进行加1的操作。也就是算上\0.

    如果不加,最后的结果为

    memset 和 memcpy

    二者的区别是

    memset是将第二个参数的值赋给第一个参数,第二个参数是一个数值类型的数。像什么unisgned char,unsigned int ,int ,long 等等。第一个参数是数组类型的数。

    memcpy的第一个和第二个参数都是数组类型的。

    他们的第三个参数是一样的。都是表示传输的字节数,所以这里土建使用unsigned char 类型的。

    #include <stdio.h>
    #include <string.h>

    int main()
    {
        unsigned char arr[6] = "abcde";
        unsigned char arr1[6];

        memcpy(arr1, arr, 6);

        printf("%s\r\n", &arr1[2]);
        printf("%s\r\n", arr1);
        printf("%s\r\n", &arr);

    }

    可以看到%s可以打印数组当前以及当前之后的内容。

    10.为什么单片机主频的时钟频率优先选择HSE而不是HSI?

  • HSI内部8MHz的RC振荡器的误差在1%左右,内部RC振荡器的精度通常比用HSE(外部晶振)要差上十倍以上。
  • 内部RC频率受温度影响比较大,如果省电Sleep模式下内部RC会停止工作。
  • 疑问:1.stm32f103c8t6是如何选择为外部时钟的

    2.选择好之后如何通过PLL配置时钟

    我这里只看到 在stm32f10x.h文件下 第115行

    #if !defined  HSE_VALUE

      #ifdef STM32F10X_CL

      #define HSE_VALUE ((uint32_t)25000 000)
    #else

      #define HSE_VALUE ((uint32_t)8000 000)

    #endif

    #endif

    这里的意思只是说如果STM32单片机的型号是STM32F10X_CL的话,配置主频为25Mhz的时钟频率,如果不是这个型号的话,就配置为8Mhz的频率。但具体的细节比如说用PLL配置成这样的。

    如何让NRST引脚输出低电平复位?(猜想)

    1.输入电压小于1V(正常是3.3V)

    2.外加一个低电平试一下

    单片机如何实现软件复位?

    作者:1个程序源

    物联沃分享整理
    物联沃-IOTWORD物联网 » C语言和单片机学习中零散的知识点

    发表回复