数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 311|回复: 34

[STM] 七十多岁,说说C语言的学习分享

[复制链接]
发表于 昨天 10:13 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
本帖最后由 qqxjyxjy 于 2025-8-24 21:01 编辑

早春时节,我下载了野火电子计算机学习资料,未曾料到这是一场与广式普通话漫长的讲座,四百学时的全阶教程铺陈眼前,主讲老师岭南腔调如温润的三月梅雨般绵密,初听时直教人坐立难安。病中困守书斋的日子里,这带着荔湾茶香的声线竟成了我消磨时光,穿越知识迷宫的引路线。

随着时光的流逝,我对他的语调习为常,渐渐地融入了他描绘的世界,他广博的计算机知识,融会贯通,学贯中西,不拘一格,深入浅出的讲解,让我深感意外。有时有点啰嗦,但细微之处讲得相当的详尽,初学者无障碍学习,让人十分轻松。让人惊叹的是,在一些复杂纷繁事件中,他竟能筛选出一些毫不相干的数据,通过排列组合,组成具体的数据结构,找出共有的规律,并用计算机语言,编写成函数和应用模块,寻觅要达到的初衷和目的。

以前那些傅里叶变换.拉普拉斯变换等桀骜不驯的东东,一长串无穷尽的多项式以及三角函数着实让人头痛,线性代数多维方程组矩阵的求解,以及微分.偏微分方程的求解,令人恐惧的计算量和复杂性及技巧性,让常人难以想象。而计算机通过简单的循环和迭代,仰仗着巨量的存储单元和惊人的计算速度,让这一切变得可行,以前需要高度复杂的技巧性,而今变得简单粗暴,只需抡起大锤不停地砸,不断地循环迭代,结果就出来了,诸多让人头痛的问题迎刃而解。

随着学习的深入,老师为我们引见了常闻其声未见其面的库函数,以前借助她的力量,走了不少的捷径,如今才知,她是如此的惊艳,美若天仙,简洁的结构,清晰的条理,巧夺天工的数据结构,经典的编程语句,让人爱不释手,难以忘怀。她的稳健和可靠性堪称一流,每当攀登悬崖峭壁时,我们都毫不犹豫的把保险绳交到她的手里,让她呵护和助力我们在崇山峻岭中穿行。
感谢她的护佑,更感谢那些无数前辈高人们呕心沥血缔造了如此完美的她。
在计算机学习这条漫漫长路上,最让人头疼的当属编译软件。他仿佛被云雾笼罩,布满机关暗道,诡秘刁钻,似笑非笑,让人捉摸不透。码农们一生的心血,大多耗费在与他的 “周旋” 之中。

计算机学科具有极强的实践性,不仅要学习理论知识,更重要的是投身实践,在实战中摸爬滚打,才能领悟其中的奥秘,将知识真正转化为自身的能力。老师首先让我们尝试编写库函数,在这个过程中熟悉库函数的结构和编译方法。库函数通常由地球屈指可数的知名芯片生产厂家组织计算机顶尖高手编写,若能自己编写,日后依靠库函数编写应用程序时,自然会底气十足,受益良多。

说来容易,实际操作起来却是一步一坑。小坑或许花上一两天便能爬出,大坑则可能需要十天半月,常常把我弄得满身泥水、狼狈不堪。这编译软件实在太过诡异,稍不留意就会落入它的 “圈套”,让人哭笑不得。他过于 “自恋”“内敛”,将自己包裹得严严实实,让我们难以探究其真实结构和内心想法。其实他不断地给予提示和暗示,奈何我愚钝,难以知晓自己错在何处。现实世界亦是如此,脚下道路千万条,其中许多都是死胡同。广袤无垠的原野上,不知隐藏着多少陷阱和沼泽,身为凡夫俗子,犯错在所难免,少犯同样的错误便是万幸。我偷偷查看了启动文件的汇编代码,那里布满了众多陷阱。并非它居心不良,犯错本就是人类的天性,无需回避,无声无息地迷失,远比掉进陷阱更为可怕。

当我一次又一次从困境中艰难爬出,似乎渐渐明白了,感觉它并非恶意。那些看似恶意的陷阱,实则是先贤用机器码镌刻的《神曲》注疏。
在黑暗中前行,不必纠结吃过的苦、受过的伤,放下一贯正确的幻想,憧憬明天黎明的曙光。试遍所有错误的路,剩下的路便任你自由翱翔。
病卧家中无聊之际,本想写写生活中的点点滴滴,奈何力不从心,词不达意,七十多岁了,怎么会连自己的母语都还讲不利落,道不明,着实惭愧。出于防呆的目的,只好硬着头皮搞笑了,还望大家海涵,见笑了。

打赏

参与人数 4家元 +100 收起 理由
慕名而来 + 30 優秀文章
飞向狙沙 + 30 謝謝分享
梅花一党 + 10 熱心會員
兔包公 + 30 以資鼓勵

查看全部打赏

发表于 昨天 10:47 | 显示全部楼层
给老先生点赞。。。。。。
回复 支持 反对

使用道具 举报

发表于 昨天 10:54 | 显示全部楼层
如果年龄大c语言啃不动
单片机上可以尝试mpython和arduino
至少对硬件简化了很多,基本应用自己玩足够了
回复 支持 1 反对 0

使用道具 举报

发表于 昨天 10:54 | 显示全部楼层
活到老 学到老,  

敬礼致意 ~~~
回复 支持 反对

使用道具 举报

发表于 昨天 10:59 来自手机浏览器 | 显示全部楼层
文中所提的什么鬼三角函数那段那些词语,俺这小白真是闻所未闻,可想而知懂这些的得是什么级别的人物呀
回复 支持 1 反对 0

使用道具 举报

发表于 昨天 10:59 | 显示全部楼层
您写作文的水平一定是一流的,导致这篇文章看上去更像是野火的软文小广告
回复 支持 1 反对 0

使用道具 举报

发表于 昨天 11:00 | 显示全部楼层
活到老 学到老,  

敬礼致意 ~~~

2025~2027 A股一次大牛市要来,

做好战斗准备了没有?
回复 支持 反对

使用道具 举报

发表于 昨天 11:20 | 显示全部楼层
七十岁啊,真的是学到老活到老。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
回复 支持 反对

使用道具 举报

发表于 昨天 11:37 | 显示全部楼层
这是实践经验的总结啊!
回复 支持 反对

使用道具 举报

发表于 昨天 11:39 | 显示全部楼层
这帖子写的   感觉是出自年方四十风华正茂之手啊

老哥过了九十再考虑防呆措施吧  




回复 支持 反对

使用道具 举报

发表于 昨天 13:55 | 显示全部楼层
猪小呆 发表于 2025-8-24 10:59
您写作文的水平一定是一流的,导致这篇文章看上去更像是野火的软文小广告 ...

小呆的眼睛是开过光的么?



二○二五年八月二十四日
回复 支持 反对

使用道具 举报

 楼主| 发表于 昨天 14:52 | 显示全部楼层
本帖最后由 qqxjyxjy 于 2025-8-24 19:39 编辑

谢谢大家的鼓励和指点,十分感谢。学习中我的体会是:通过书本中学习固然重要,更重要的是通过实践,做一些简单的项目提升最为显著。尤其是向AI请教非常必要,AI条理清晰,不厌其烦,诲人不倦,知识全面。下面就是我向DeepSeep请教的对话:
回复 支持 反对

使用道具 举报

 楼主| 发表于 昨天 15:08 | 显示全部楼层
Qqxjyxjy清风:define+digitalToggle(p,i)+{p->ODR+^=i;怎么理解?

DeepSeek: 这段代码看起来像是针对嵌入式系统(特别是STM32微控制器)的GPIO(通用输入输出)操作,但有一些语法问题需要澄清。我来解释其意图和正确的写法:
原始代码分析:
c
复制
下载
define+digitalToggle(p,i)+{p->ODR+^=i;
问题:
1.        缺少#符号(如果是宏定义)
2.        奇怪的+运算符(可能是输入错误)
3.        ^=运算符位置错误(+^=应该是^=)

正确理解:
这应该是一个用于切换GPIO引脚状态的宏或函数,正确的形式可能是:
c
复制
下载
#define digitalToggle(p, i) { p->ODR ^= i; }

解释:
1.        p->ODR:访问GPIO端口的输出数据寄存器
o        p是指向GPIO端口的指针(如GPIOA)
o        ODR是Output Data Register的缩写
2.        ^=:按位异或赋值操作
o        异或特性:相同为0,不同为1
o        例如:如果某位原来是1,异或1后会变为0;原来是0则变为1
3.        i:表示要切换的引脚掩码

例如:GPIO_PIN_5表示第5引脚


完整示例:
c
// 切换GPIOA的第5引脚状态
digitalToggle(GPIOA, GPIO_PIN_5);

等效展开:
c
{ GPIOA->ODR ^= GPIO_PIN_5; }


更安全的写法建议:
c
#define digitalToggle(p, i) do { (p)->ODR ^= (i); } while(0)
这种写法可以避免宏在if/else语句中使用的潜在问题。
工作原理:
假设ODR某位的当前状态是1,异或1后变为0;如果是0,异或1后变为1,从而实现电平切换。


qqxjyxjy清风:谢谢了。

DeepSeek: 不客气!
回复 支持 反对

使用道具 举报

发表于 昨天 15:26 | 显示全部楼层
楼主是不是从矿坛找到这里的。
回复 支持 反对

使用道具 举报

 楼主| 发表于 昨天 15:32 | 显示全部楼层
梅花一党 发表于 2025-8-24 15:26
楼主是不是从矿坛找到这里的。

我是最近买了一台众仪ZT-225,4位半数字表,出现白屏.死机,发现这里有关众仪ZT-225的信息,才找到这里。
回复 支持 反对

使用道具 举报

发表于 昨天 16:51 | 显示全部楼层
行动力真强啊,我连面包板的书都看不进去
回复 支持 反对

使用道具 举报

发表于 昨天 18:18 | 显示全部楼层
活到老 学到老,   
我辈楷模啊.
回复 支持 反对

使用道具 举报

 楼主| 发表于 昨天 19:32 | 显示全部楼层
本帖最后由 qqxjyxjy 于 2025-8-25 11:45 编辑
sumandigit 发表于 2025-8-24 18:18
活到老 学到老,   
我辈楷模啊.

qqxjyxjy清风:再问DeepSeek,  p是在STM32哪个文件中定义的指针?


DeepSeek:在STM32的标准外设库(Standard Peripheral Library)或HAL/LL库中,p 本身并不是预定义的全局指针,而是用户或开发者自定义的函数参数或宏参数,通常指向某个GPIO端口(如GPIOA、GPIOB等)。它的定义位置取决于具体代码的上下文。以下是详细分析:
________________________________________
1. p 的可能来源
(1)用户自定义函数或宏中的参数
例如在以下代码中,p 是函数的形参,指向GPIO端口:
c

void DigitalToggle(GPIO_TypeDef* p, uint16_t i) {
    p->ODR ^= i;  // p是用户传入的指针,如GPIOA
}
调用时需手动传入指针:
c
DigitalToggle(GPIOA, GPIO_PIN_5);  // GPIOA就是实际参数
(2)宏定义中的参数
如果是宏定义(如你之前提到的digitalToggle),p 是宏的参数,由调用者指定:
c
#define digitalToggle(p, i)  do { (p)->ODR ^= (i); } while(0)
// 调用时:
digitalToggle(GPIOB, GPIO_PIN_3);  // p被替换为GPIOB
________________________________________
2. GPIOx(如GPIOA、GPIOB)在哪里定义?
STM32的GPIO端口指针(如GPIOA、GPIOB)是芯片厂商预定义的全局指针,定义在以下位置:
•        标准外设库(Standard Peripheral Library):
文件:stm32f10x.h(或其他系列,如stm32f4xx.h)
定义示例:
c

#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
其中 GPIOA_BASE 是GPIOA的内存映射地址(在芯片参考手册中指定)。
•        HAL/LL库:
文件:stm32fxxx.h(如stm32f1xx.h)
定义方式类似,通过外设基地址映射。
________________________________________
3. 如何找到这些定义?
1.        查看库文件:
o        在工程中搜索 GPIO_TypeDef 或 GPIOA,通常会定位到 stm32fxxx.h。
o        例如,STM32F1的标准库中,GPIOA 定义在 stm32f10x.h。
2.        参考芯片手册:
o        在芯片的《参考手册》(Reference Manual)中查找 “Memory Map” 章节,可以看到外设基地址(如 GPIOA_BASE = 0x40010800)。
3.        跳转定义(IDE技巧):
o        在Keil/IAR/VSCode中,按住 Ctrl 键点击 GPIOA,会自动跳转到其定义。
________________________________________
4. 总结
•        p 是用户代码中的参数或宏参数,不是库预定义的。
•        GPIOx(如GPIOA) 是库预定义的指针,定义在芯片头文件(如stm32f10x.h)中,指向芯片设计时固定的外设地址。
如果有具体代码场景或库版本,可以进一步分析!

qqxjyxjy清风:p 是宏的参数,由调用者指定,p不需调用者事先声明为指针,调用者认为是指针就是指针,对吗?

DeepSeek: 你的理解基本正确!在宏定义中,p 作为宏参数,它的“指针属性”完全依赖于调用者传入的实际参数,而宏本身不会强制检查类型。以下是详细解释:
________________________________________
1. 宏的本质:文本替换
宏在预处理阶段会被直接展开为代码,不做任何类型检查。例如:
c

#define digitalToggle(p, i)  do { (p)->ODR ^= (i); } while(0)
当调用 digitalToggle(GPIOA, GPIO_PIN_5) 时,预处理器会直接替换为:
c

do { (GPIOA)->ODR ^= (GPIO_PIN_5); } while(0)
•        关键点:
o        p 是否合法完全取决于调用者传入的是什么。
o        如果传入 GPIOA(库预定义的指针),代码正确;如果传入一个整数或非指针变量,编译时会报错(因为试图用 -> 访问非指针类型)。
________________________________________
2. 调用者的责任
•        调用者必须确保 p 是指针(尤其是 GPIO_TypeDef* 类型),但不需要显式“声明”它是指针,因为:
o        在STM32库中,GPIOA、GPIOB 等已经是预定义好的指针常量(类型为 GPIO_TypeDef*)。
o        如果用户传入其他类型(如 int),编译器会报错。
•        示例对比:
c

// 正确:GPIOA是库预定义的指针
digitalToggle(GPIOA, GPIO_PIN_5);

// 错误:5不是指针,编译时报错
digitalToggle(5, GPIO_PIN_5);  // 展开后:5->ODR ^= ... → 语法错误
________________________________________
3. 为什么不需要显式声明?
•        宏的灵活性:宏只是文本替换工具,不涉及语法分析。它假设调用者清楚参数的类型。
•        库的设计:STM32库已通过头文件(如 stm32f10x.h)将 GPIOA 等定义为指针,用户直接使用即可。
________________________________________
4. 潜在风险与建议
•        风险:如果调用者传错参数(如非指针),编译器可能报晦涩的错误(例如 -> 操作符用于非指针类型)。
•        建议:
1.        使用类型检查:如果是函数而非宏,可以用 GPIO_TypeDef* 明确参数类型:
c

void DigitalToggle(GPIO_TypeDef* p, uint16_t i) {
    p->ODR ^= i;  // 编译器会检查p的类型
}
2.        添加注释:在宏定义处注明参数类型要求:
c

// p必须为GPIO_TypeDef*类型,i为引脚掩码(如GPIO_PIN_5)
#define digitalToggle(p, i) ...
________________________________________
5. 总结
•        你的理解正确:调用者无需事先声明 p 是指针,但必须传入实际指针(如 GPIOA)。
•        本质原因:宏不检查类型,它依赖调用者遵守隐式约定(“认为是指针就是指针”)。
•        安全建议:在团队项目中,建议用函数或静态内联函数替代宏,以利用编译器的类型检查。
如果有具体代码场景需要分析,可以随时讨论!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-8-25 18:32 , Processed in 0.156000 second(s), 10 queries , Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表