C语言学习笔记
文章平均质量分 71
分享在学习C语言的过程中遇到的问题及解决方法
嵌入式@hxydj
这个作者很懒,什么都没留下…
展开
-
嵌入式编程时如何分层设计代码
代码量少的话还可以,如果代码里比较多,需要在一堆代码中去找这些需要的代码也很费时间,假如某一个功能需要升级了,代码需要修改了,那么每个产品都需要找到对于的代码去修改,那工作量岂不是很大。串口数据接收的函数,由统一的一个函数就被分离成了 串口中断----->数据接收 两个部分,如果后面还需要对接收到的数据进行内部解析,按照这种思维再抽象一个协议解析的函数。将接收函数的参数,由一个修改为2个。将接收到的数据存储到数组中,并使用一个值来标记接收到数据的状态和接收到数据个数,如果收到回车换行符,就结束数据接收。原创 2024-12-22 13:59:28 · 638 阅读 · 0 评论 -
STM32CubeMX生成代码后汉字乱码解决方法
总的来说汉字乱码的原因就是UTF-8和GB2312这两种格式再IAR这个编译器中不会自动转换,需要使用vscode软件手动转换代码格式。这是由于,STM32CubeMX生成的代码默认的是UTF-8格式,这里虽然把编译器的编码格式改了,但是代码本身其实还是UTF-8格式。这个打印函数是在main.c文件中,在VScode 使用UTF-8格式打开,然后编码格式另存为GB2312。此时代码中的汉字是正常的,接着再点击右下角的编码格式。此时代码的注释是汉字,但是使用IAR软件打开之后汉字是乱码。原创 2024-12-19 21:55:14 · 213 阅读 · 0 评论 -
IAR中如何而将定义的数组放在指定的位置
注意添加的第一行代码中内存地址的范围要在RAM地址范围之内,否则就会出错。使用这种方法的话,就不需要修改 stm32f103xb_flash.icf 这个文件内容了,直接使用默认的内容就行。但是这个方法在IAR中是用不了的,通过网上查找各种资料,发现了两种可用的方法。我这里测试的单片机是stm32f103c8t6,其他单片机的操作方法是一样的。这里要注意一个问题,如果使用第2种方法的时候,数组大小必须是4的倍数,否则编译会报错。好了,这两种方法就分享到这,如果后面发现了其他新的方法再补充。原创 2024-12-19 21:04:35 · 266 阅读 · 0 评论 -
关于CAN通信中同步机制的通俗理解
所以在上升沿或者下降沿的过程中有识别你是0,也有可能识别你是1,这样判断你的电平的时候就可以引起误差。现在唯一确定的时间就是你数数是1秒钟变化1次的,那么在你变化的一瞬间我就开始对你计时,然后在对比我自己数数的计时,这样就可以知道我和你的时间差是多少,如果我比你慢,那么下次我就提前变,如果我比你快,我就晚一点变。比如上图中的状态,当你从0变为1的时候,我开始计时,我这边开始变的时候我也计下时间,此时我就发现了,你比我快了0.1秒,那我我此时就可以推算出,如果你到了1秒钟变化的时候,我才数到0.9秒。原创 2024-12-14 16:35:40 · 502 阅读 · 0 评论 -
STM32F103单片机HAL库串口通信卡死问题解决方法
所以分析造成串口卡死或者数据丢失的原因主要原因应该是直接在接收中断中直接发送数据,由于是接收一个字节,立即发送一个字节,如果每次发送几十个字节的时候,每两个字节之间的时间是很短的。在接收回调函数中接收到数据之后先存放到数组之中,当收到回车换行符之后结束接收,然后在main函数中检测接收标志位,如果接收完成,再将接收的数据打印出来。在网上查资料发现造成这个原因主要是HAL的流程问题,当串口在发送数据的时候,如果又接收到了数据,程序中就会出现死锁的情况。所以就将正点原子的串口接收方法移植过来。原创 2024-12-09 21:40:58 · 971 阅读 · 0 评论 -
STM32F103单片机使用STM32CubeMX创建IAR串口工程
最后编译下载程序,在串口回调函数中打个断点,使用USB转TTL连接单片机的PA10和PA9引脚,使用串口工具发送一个字母q,可以看到串口中断已经进去了,接收到的数据也是q。接下来设置时钟,选择时钟配置,左下角时钟源选择HSE,然后在HCLK的位置直接输入需要输出的时钟值,这里设置72,按回车键。接下里设置串口参数,这里直接使用默认值,波特率115200,8位数据位,1位停止位,无校验位,发送和接收模式。接下来设置工程名,选择工程存储的位置,设置编译器的类型,和编译器最低版本。这里的编译器选择为IAR。原创 2024-12-08 18:03:42 · 479 阅读 · 0 评论 -
STM32F103单片机使用STM32CubeMX新建IAR工程步骤
STM32F103单片机上,使用STM32CubeMX生成IAR工程设置步骤。原创 2024-12-07 22:24:34 · 418 阅读 · 0 评论 -
使用CCS软件查看PID曲线
这是因为取消延时后,图形显示的刷新率太低,不能实时捕获到每个数据,所以直接显示变量的曲线就看不出来细节了,而数组显示比较完整,是因为代码中将过程中的所有数据都先存储到了数组中,然后根据数组中的数据来绘制曲线。这是两种很典型的PID算法,一个位置式,一个增量式。此时又增加了一个显示窗口,但是这两个窗口重叠在了一起,看起来不方便,在窗口标题处按住鼠标左键,拖动窗口,将两个图形显示调节到合适的位置。添加完成之后,将黄色箭头的图标选中,这个是实时刷新功能,这样在代码全速运行的时候,就可实时看到变量值在变化。原创 2024-04-17 22:45:00 · 2043 阅读 · 1 评论 -
STM32F407单片机HAL库CAN2不能接收数据解决方法
下面就就是can的时钟,这里要注意一个就是使用can1的时候,开启can1的时钟就行,但是使用can2的时候,也必须开启can1的时钟。在can的控制器中,存储访问控制器是由can1控制的,当使用can2的时候,can2要访问存储访问控制器时,必须通过can1才能访问,所以使用can2的时候,can1为主机,can2为从机。当使用can2的时候,直接将值设置为14。的值就很关键了,这个值的含义是,从机滤波器的起始地址,那么当使用can2时,can2就是从机,那么这个值就是can2滤波器的起始地址,而上面。原创 2023-05-27 17:08:41 · 3302 阅读 · 2 评论 -
PSIM软件BUCK转换数字控制官方例程
在使用PSIM软件仿真开关电源时,大多数都是模拟电路,纯数字电路的仿真很少。无意间发现了在PSIM 2021版本中有官方的数字控制BUCK电路仿真。电路使用简单C模块编写的代码来控制电路。 由于下载的2021版是演示版,不能直接仿真,为了能够彻底的学习,于是将电路图和程序移植到了9.1版本中。现在将电路和代码分享出来。 2021版官方例程 由于软件是演示版,有限制,所以不能仿真。 于是将电路图和代码移植到 PSIM 9.1 版本上 硬件电路如下: 首先使用电路传感器读取电原创 2022-03-01 16:44:02 · 5796 阅读 · 4 评论 -
C语言学习笔记---字符函数isalnum()和iscntrl()
isalnum() 函数用于检查所传的字符是否是字母或者十进制数字。它的函数原型如下: _CRTIMP int __cdecl isalnum(int _C); 返回值为非零(真)表示参数c是字母或者十进制数字,返回值为零(假)表示参数c既不是十进制数字,也不是字母。 下面通过一个简单的例子来演示它的用法。#include <stdio.h>#include <ctype.h>int main(){ int var1 = 'a'; int va原创 2022-02-17 08:45:45 · 449 阅读 · 2 评论 -
C语言学习笔记---浮点函数floor()和ceil()和浮点数四舍五入
在上一篇文章中已经了解了浮点数在计算机中的存储原理,同时也介绍对浮点数取整和取余的函数.在C语言标准库里面还提供了对于浮点数取整的两个函数。函数原型如下: double __cdecl ceil(double _X); double __cdecl floor(double _X); ceil函数返回大于或等于 x 的最小的整数值。下面通过一个简单的例子,演示一下ceil函数的用法。#include <stdio.h>#include <math.h>int原创 2022-02-17 08:43:59 · 1570 阅读 · 0 评论 -
C语言学习笔记---浮点函数modf()和fmod()
modf函数可以提取出浮点数的整数部分和小数部分。fmod函数可以返回两个浮点数相除的余数。它们的函数原型如下: double __cdecl modf(double _X,double *_Y); double __cdecl fmod(double _X,double _Y); 这两个函数的功能看起来都挺简单的,但是为什么在C语言库中还要专门搞一个函数来计算呢?在使用这两个函数之前,首先看一个简单的浮点数相关的例子。int main (){ int i; float j=1.0原创 2022-02-17 08:42:42 · 3696 阅读 · 0 评论 -
C语言学习笔记---三角函数
在C语言标准库里面提供了常用的三角函数,在头文件math.h里面可以看到函数的相关定义。 double __cdecl sin(double _X); double __cdecl cos(double _X); double __cdecl tan(double _X); double __cdecl asin(double _X); double __cdecl acos(double _X); double __cdecl atan(double _X); double原创 2022-02-17 08:41:18 · 20391 阅读 · 4 评论 -
C语言学习笔记---字符串转浮点函数
字符串不仅可以转换为整数,也可以转换为浮点数,字符串转浮点数函数原型如下: float __cdecl __mingw_strtof (const char * __restrict__, char ** __restrict__); double __cdecl __mingw_strtod (const char * __restrict__, char ** __restrict__); strtof函数返回值是一个单精度浮点数,strtod返回值是一个双精度浮点数。原创 2022-02-17 08:40:24 · 14946 阅读 · 3 评论 -
C语言学习笔记---字符串转换函数
字符串转整数 字符串转换为整数的函数有两个,他们的函数原型如下: int __cdecl atoi(const char *_Str); long __cdecl atol(const char *_Str); 这两个函数的用法都很简单,atoi函数将字符串转换为整数int型,atol函数将字符串转换为长整形long int型。如果转换无效,返回值都为0,下面通过一个简单的例子看一下这两个函数的用法。#include <stdio.h>#include <stdlib原创 2022-02-17 08:36:43 · 1094 阅读 · 0 评论 -
C语言学习笔记---时间函数strftime()
strftime函数主要用于时间格式化,它的函数原型如下:size_t __cdecl strftime(char * __restrict__ _Buf,size_t _SizeInBytes,const char * __restrict__ _Format,const struct tm * __restrict__ _Tm);它有4个参数:_Buf, 表示返回的时间字符串_SizeInBytes, 要写入的字节的最大数量_Format, 这是 C 字符串,包含了普通字符和特殊格式说明符原创 2022-02-16 13:56:30 · 1902 阅读 · 0 评论 -
C语言学习笔记---时间函数mktime()和difftime()
这两个函数原型如下: __CRT_INLINE time_t __cdecl mktime(struct tm *_Tm); __CRT_INLINE double __cdecl difftime(time_t _Time1,time_t _Time2);mktime函数 mktime函数会把参数把 timeptr 所指向的结构转换为自 1970 年 1 月 1 日以来持续时间的秒数,如果发生错误时则返回-1。 参数结构体原型如下:struct tm { int tm_sec;原创 2022-02-16 13:55:15 · 1264 阅读 · 0 评论 -
C语言学习笔记---时间函数ctime()和gmtime()
函数原型如下: __CRT_INLINE char *__cdecl ctime(const time_t *_Time); __CRT_INLINE struct tm *__cdecl gmtime(const time_t *_Time);ctime函数 ctime函数可以将当前时间值转换为字符串格式返回。返回的字符串格式为:Www Mmm dd hh:mm:ss yyyy 其中,Www 表示星期几,Mmm 是以字母表示的月份,dd 表示一月中的第几天,hh:mm:ss 表示时间,yy原创 2022-02-16 13:54:08 · 1082 阅读 · 0 评论 -
C语言学习笔记---时间函数asctime()和localtime()
这两个时间函数原型如下: char *__cdecl asctime(const struct tm *_Tm); __CRT_INLINE struct tm *__cdecl localtime(const time_t *_Time);asctime函数 asctime函数可以将时间结构体转换为时间字符串格式。它的参数是一个时间结构体。结构体原型如下:struct tm { int tm_sec; /* 秒,范围从 0 到 59原创 2022-02-16 13:52:49 · 3481 阅读 · 0 评论 -
C语言学习笔记---时间函数clock()和time()
时间函数在编写代码的时候会经常用到,下面就来总结一下clock和time函数的用法,函数原型如下: clock_t __cdecl clock(void); __CRT_INLINE time_t __cdecl time(time_t *_Time); clock函数 clock函数返回程序从运行开始到当前调用函数位置处CPU所使用的时间,通过这个函数就可以计算某段代码运行时所使用的时间。 下面通过一段简单的代码测试一下:#include <stdio.h>#i原创 2022-02-16 13:51:42 · 1990 阅读 · 0 评论 -
C语言学习笔记---随机数rand()函数
在生活中很多场景下都需要产生随机数,比如抽奖,打牌,游戏等场景下就需要使用随机数。在C语言标准库函数里面有专门用来产生随机数的函数rand,它的函数原型如下: int __cdecl rand(void); rand函数没有参数,它的返回值就是随机数。下面通过一个简单的例子来测试一下rand函数。#include <stdio.h>#include <stdlib.h>int main(int argc, char** argv) { int i;原创 2022-02-16 13:50:09 · 1494 阅读 · 0 评论 -
C语言学习笔记---abs()函数和div()函数
C语言库中提供了许多函数,这样需要计算的时候,可以直接借助库函数,而不用自己重新编写函数。今天就来看一下C语言标准库函数里面的整型函数。 int __cdecl abs(int _X); long __cdecl labs(long _X); div_t __cdecl div(int _Numerator,int _Denominator); ldiv_t __cdecl ldiv(long _Numerator,long _Denominator); abs函数用来计算整数的绝原创 2022-02-16 13:48:58 · 721 阅读 · 0 评论 -
C语言学习笔记---常见的动态内存错误
在使用动态内存分配时,经常会出现许多错误。这些错误包括对NULL指针进行操作,对分配内存操作时越过边界,释放非动态内存分配的内存,一块动态内存被释放后继续使用等。 动态分配内存最常见的错误就是忘记检查所请求的内存是否分配成功。为了避免每次分配内存后忘记检查,现在通过一段代码来有效的避免这个问题。#define MALLOC(num,type) (type *)alloc( (num) * sizeof(type))void *alloc(size_t size) { void *new_me原创 2022-02-16 13:47:44 · 539 阅读 · 0 评论 -
C语言学习笔记---动态内存分配calloc()和realloc()
动态内存分配函数除了常用的malloc函数以外,还有calloc函数和realloc函数,这两个函数的原型如下:void *__cdecl calloc(size_t _NumOfElements,size_t _SizeOfElements);void *__cdecl realloc(void *_Memory,size_t _NewSize);calloc函数 calloc函数用于动态的分配内存,并初将其始化为0。它有两个参数,第一个参数用于设置连续空间的数量,第二个参数用于设置每个原创 2022-02-16 13:46:37 · 615 阅读 · 0 评论 -
C语言学习笔记---动态内存分配
数组在内存中时存储在连续的位置上,当声明一个数组的时候,编译器就会在内存中分配它所需要的空间,但是有时候还需要使用动态内存为数组分配空间。 比如现在要同统计一个班级学生的成绩,可以申请一个固定大小的数组,但是班级学生的人数时会变动的,当这个固定大小的数组设置的太小时,有可能不能完全放下所有学生的数据,当这个数组设置的太大时,不一定每次每次都能用到这么大的空间。导致系统空间的浪费。 为了使程序更加灵活,同时更加有效的利用内存空间,通常使用动态内存分配来解决这个问题,C库函数里面提供了两个函数mal原创 2022-02-16 13:45:11 · 464 阅读 · 1 评论 -
IAR软件中直接查看编译后代码大小
在使用IAR软件编译代码时,编译后往往看不到编译后代码的大小情况。在调试程序的时候还是比较麻烦的。下面就总结两种最简单的方法在IAR编译器中查看代码大小。通过输出的编译信息查看 IAR默认的编译信息输出很少,基本就只能查看一下错误和警告的数量。 如果想要输出更多的编译信息,在这个Messages信息框中,单击鼠标右键选择All。 这时再重新编译一次代码。 此时就可以看到编译信息输出了很多,其中就包括了代码占用空间情况。这样通过一个简单的设置就可直接在编译器输出信息中查看代码的大小原创 2021-12-06 11:44:52 · 5298 阅读 · 1 评论 -
STM8单片机串口同时识别自定义协议和Modbus协议
在单片机开发中,串口是最常用的和外界交换数据的渠道,要使用串口,那必不可少的就是通信协议,通信协议就是单片机和外界通信的语言,要想正常和其他设备正常交流,首先语言必须相通。 在实际开发过程中由于各种原因,导致很多时候单片机和外界其他设备协议不兼容,在使用的时候就比较麻烦。比如单片机要和两个设备通信,但是这两个设备的通信协议的不一样,在使用时单片机就必须使用两个串口分别和两个设备通信。如果这两个设备同时使用时还不感觉到资源浪费,如果每次只接一个设备,那么另一个串口也不能作为其他功能使用,还得留着备用。原创 2021-12-03 21:16:58 · 6256 阅读 · 6 评论 -
在STM8单片机中自己实现 printf()函数功能
由于STM8单片机本身内存比较小,而系统自带的printf()函数又比较占据空间,所以在稍微大一点的工程中有时候一使用 printf() 函数就会导致单片机内存不足,于是想着能不能自己写一个比较小的函数来实现类似printf()函数的功能。经过网上查找资料和总结终于找到了一个占用内存比较小,又能实现串口打印功能的方法。 现在将自己的方法分享出来,这里使用 STM8S003F3P6单片机测试。 首先新建一个工程,专门用来测试串口功能。 串口部分相关代码如下://串口void Uart1_I原创 2021-12-03 15:58:39 · 2847 阅读 · 0 评论 -
C语言学习笔记---可变参数
在C语言中有些函数传递进入的参数数量是不固定的,最常见的就是**printf()**函数,它就是利用可变参数来实现任意格式的数据打印功能。 我们自己也可以编写一个参数可变的函数,每次动态的检测传入的参数,然后执行相应的动作。...原创 2021-11-12 20:41:20 · 1160 阅读 · 0 评论 -
C语言学习笔记---数据拷贝函数memcpy()和memmove()函数
在C语言中拷贝字符串的时候通常可以使用strcpy()函数和strncpy()函数,这两个函数是专门针对字符串拷贝的。如果想要拷贝其他类型数组的话,可以使用memcpy()和memmove()函数。下面就来看一下这两个函数的使用方法。memcpy()函数 函数原型如下:void * __cdecl memcpy(void * __restrict__ _Dst,const void * __restrict__ _Src,size_t _Size) 这个函数有三个参数,第一个是目标数组的原创 2021-11-12 14:05:41 · 2435 阅读 · 0 评论 -
C语言学习笔记---断言函数assert()
在C语言库函数中提供了一个辅助调试程序的小型库,它是由assert()宏组成,接收一个整形表达式作为参数。如果表达式的值为假(非零),则assert()宏就在标准错误流(stderr)中写入一条错误信息,并调用abort()函数终止程序。 下面通过一个简单的例子来看一下assert()的用法。int main(){ int n = 1; assert(n>=0); printf("%d \r\n",n); system("pause"); return 0;} 在asse原创 2021-11-12 11:09:05 · 3052 阅读 · 4 评论 -
C语言学习笔记---C库排序函数qsort()
在处理数据的时候,由于排序方法会经常用到,所以C语言的库函数里面自带了快速排序的函数qsort(),对大型数据而言,“快速排序”方法是最有效的排序方法之一。它是把数组不断的分成更小的数组,直到变成单元素数组。首先把数据分成两部分,一部分的值都小于另一部分的值。这个过程一直持续到数组完全排好序为止。 其函数原型如下所示:void __cdecl qsort(void *_Base,size_t _NumOfElements,size_t _SizeOfElements,int (__cdecl *_原创 2021-11-11 16:40:29 · 1065 阅读 · 0 评论 -
C语言学习笔记---位字段
在C语言中通常操作只有真假两种状态的的数据时使用布尔bool变量比较多,如果需要同时观察多个状态,这时候选择位操作效率会更高,用一个字节的8位分别表示8种状态。比较节省内存,处理起来效率更高。但是这种方法有一个缺点,就是看起来不直观,比如0x5C要想知道每一位的状态还得换算一下,同时要操作某一个单独位时,还必须使用位操作运算,比如位与、位或、异或。 如果熟悉单片机的就会想到,能不能在C语言中像操作寄存器那样直接操作一个字节的单独一位呢? 比如这是一个单片机的端口方向配置寄存器,每一个端口原创 2021-11-10 13:16:24 · 2000 阅读 · 0 评论 -
C语言学习笔记---指向函数的指针
在内存中函数的存放也是一段连续的内存,函数名就是指向改内存中的首地址,所以也可以将这个函数的首地址赋给一个指针变量,这样通过指针变量就可以访问改函数。 那么为什么要通过指针来访问函数呢?下面通过一个简单的例子来演示一下。int (*fun)(int m,int n);int Add(int x,int y){ return x + y;}int Sub(int x,int y){ return x - y;}int Mul(int x,int y){ return x *原创 2021-11-09 15:48:33 · 942 阅读 · 0 评论 -
C语言学习笔记---typedef 简介
在单片机和操作系统中 typedef 会经常用到,它可以为某一个类型自定义名称。和#define比较类似。但是又有不同的地方。typedef 创建的符号只能用于数据类型,不能用于值。而#define 创建的符号可以用于值。typedef 是由编译器来解释,而不是预处理器。typedef 使用起来更加灵活。 下面使用typedef定义一个数据类型int main(){ typedef unsigned char BYTE; BYTE c = 10; printf("%d \r原创 2021-11-08 17:20:28 · 3328 阅读 · 1 评论 -
C语言学习笔记---枚举类型enum
枚举类型通常用来存放整形常量。可以使用enum关键字来创建一个"新的类型",用来表示一组关系比较密切的整形常量。 可以这样声明:enum spectrum { red,orange,yellow,green,blue,violet };enum spectrum color; 第一条语句声明创建了spectrum 作为标记名,允许把enum spectrum作为一个类型名使用。第二条语句声明了将 color作为该类型的变量。在第一条语句中依次列举了spectrum 变量可能有的值,因此co原创 2021-11-08 15:31:08 · 1095 阅读 · 0 评论 -
C语言学习笔记---结构体中的字符数组和字符指针
在结构体中可以使用字符数组来存储字符串,也可以使用字符指针来存储字符串。比如:struct str { char s1[5]; char s2[5]; }; struct str str1= {"123","abc"};或者 struct str { char *s1; char *s2; }; struct str str1= {"123","abc"}; 通过这两种方式定义的结构体都可以正常运行,输出结果也都是一样的。 那么这两种方式在内存中存储是原创 2021-11-08 14:17:15 · 2404 阅读 · 0 评论 -
C语言学习笔记---结构体作为函数参数和返回值
结构体不仅可以作为函数的参数传递,也可以作为函数的返回值。现在就分别来看一下这两种情况。结构体作为参数struct score{ int chinese; int math; int english;};int sum(struct score p){ return (p.chinese + p.math + p.english);}int main(){ int total = 0; struct score s= { 80,90,85 }; total =原创 2021-11-08 13:20:28 · 9864 阅读 · 4 评论 -
C语言学习笔记---向函数传递结构信息
传递值 在向函数传递参数的时候,可以直接将结构体成员的值传递给函数。如下面的例子:int sum(int x,int y,int z){ return (x + y + z);}int main(){ int total = 0; struct score { int chinese; int math; int english; }; struct score s= { 80,90,85 }; total = sum(s.chinese,s.math,s原创 2021-11-08 10:45:17 · 1008 阅读 · 0 评论
分享