三年模拟 发表于 2023-2-18 09:11:19

现在主流是c make

tomzbj 发表于 2023-2-18 10:56:42

三年模拟 发表于 2023-2-18 09:11
现在主流是c make
(引用自101楼)

这种所谓的主流不追也罢, Makefile到现在快50年了, 估计还能再战几十年,

qmake, scons, 个个号称要代替Makefile, 结果呢...

小弟们如果对什么新玩意儿感兴趣, 自己去学就是了, 我反正不鼓励也不反对, 个人爱好呗.

三年模拟 发表于 2023-2-18 13:17:50

本帖最后由 三年模拟 于 2023-2-18 13:21 编辑

tomzbj 发表于 2023-2-18 10:56
这种所谓的主流不追也罢, Makefile到现在快50年了, 估计还能再战几十年,

qmake, scons, 个个号称要代替 ...
(引用自102楼)

我喜欢用qmake配合qt creator的ide开发linux纯c,虽然没有宇宙第一ide vs studio舒服,但是也可以了,不用写make了,人的精力毕竟有限,写应用层还是专注代码方面,个人理解。

tomzbj 发表于 2023-2-18 19:27:17

三年模拟 发表于 2023-2-18 13:17
我喜欢用qmake配合qt creator的ide开发linux纯c,虽然没有宇宙第一ide vs studio舒服,但是也可以了,不 ...
(引用自103楼)

Makefile基本也只需要从头写一次吧,然后每次在这个基础上稍微改改就行了……

tomzbj 发表于 2023-2-24 10:44:51

2.24更新: 编译多个文件, 函数原型, 头文件, include, #ifndef避免重复包含
全局变量, 加static和extern的作用
写简单的Makefile,依赖关系, -B和-j

tomzbj 发表于 2023-3-21 15:14:36

更新:
3.3 数据结构初步, 时间和空间复杂度, 大O记号, 抽象数据类型

例题: 把一个字节按二进制逆序, 三种算法: 循环, 位运算处理, 查表
unsigned char byte_reverse(unsigned char data)
{
    unsigned char output;
    for(int i = 0; i < 8; i++) {
      if(data & (1 << i))
            output |= (1 << (7 - i));
    }
    return output;
}

unsigned char byte_reverse2(unsigned char data)
{
    data = (data >> 4) | (data << 4);
    data = ((data & 0x33) << 2) | ((data & 0xcc) >> 2);
    data = ((data & 0x55) << 1) | ((data & 0xaa) >> 1);
    return data;
}

线性表的实现, 顺序表, 链表, 静态链表

3.10: 讲上次的习题, 栈和队列的概念, 递归, 回溯, 布置作业: 八皇后, 表达式求值

哈哈龅牙仔 发表于 2023-3-21 16:23:57

TINXPST 发表于 2022-3-18 17:52
楼主应该有现成的案例,我记得你之前不是做过一个用STM32F3做的LCR的一个小工程吗,就用这个项目让你的小弟 ...
(引用自37楼)

这倒是,在项目中闯关才是锻炼最快,最有效的途径

tomzbj 发表于 2023-3-24 11:07:09

哈哈龅牙仔 发表于 2023-3-21 16:23
这倒是,在项目中闯关才是锻炼最快,最有效的途径
(引用自107楼)

从项目闯关也有缺点, 容易基本功不扎实, 以及对具体项目/公司的环境/工具链形成依赖.开发环境/工具链这些东西不是真正的本事, 要是换了环境/工具链干不了活了那就废了.

比如我一直让他们用gcc+vim+Makefile干活, 今天才让他们换到eclipse里再调Makefile, 至于以后想用vscode什么的都随意了. 要是一开始就用eclipse或者vscode那恐怕以后没那么容易换环境了.

当然从企业的角度看更喜欢这样, 耗材么. 但是咱教学生不能把人往耗材的方向教.

感觉又有点象武侠小说里对正派和邪派武功的说法, 正派子弟从打坐运气站桩开始练, 进步缓慢, 邪派这边就讲究个入门迅速. 杨过靠速成的半招蛤蟆功就能秒杀大小武和鹿清笃等人.
但如果不是他确实资质优秀, 恐怕到后期就打不过差不多水平的正派子弟了.

如果是绝顶高手那就无所谓了, 怎么学都一样.

tomzbj 发表于 2023-3-24 11:12:32

今天的内容: 现撸八皇后, 递归和非递归版本.
严格按面向对象的思路进行, 棋盘存储和所有放子、判断合法、输出操作作为一个类, 这样主函数这边就清爽多了.

tomzbj 发表于 2023-3-31 22:55:06

今天带小弟们实现的内容:从零开始, 开发板LED闪灯

1. 把之前八皇后之类项目的main.c和Makefile复制出来, Makefile里SRC变量只留一个main.c, main.c里删空, 只留一个while(1) {},然后make -B编译正常出main.exe.
2. Makefile里把gcc换成arm-none-eabi-gcc, 目标从main.exe换成main.elf, 编译正常出main.elf.
3. Makefile里加一个目标hex, 加上相应的依赖,效果是增加一条arm-none-eabi-objcopy -O ihex main.elf main.hex.
4. main.c开头增加#include "stm32f10x.h", main函数里增加

RCC->APB2ENR |= RCC_APB2Periph_GPIOA;
GPIOA->CRL &= ~(0xf << (6 * 4));
GPIOx->CRL |= (((GPIO_Mode_AF_PP & 0xf) | GPIO_Speed_50MHz) << (6 * 4));

while(1)里再放个
GPIOA->ODR ^= GPIO_Pin_6;
for(volatile int i = 0; i < 1000000UL; i++) {}

编译完下载到开发板, 不能运行。
5. 因为没有指定目标地址, 找个lds文件, 然后Makefile里链接命令加上-T xxx.lds, 再编译, 提示找不到Reset_Handler, 这是因为没有启动文件.
6. 再找个启动文件, Makefile里加上ASRC = xxx.s,AS = gcc, OBJL加上$(ASRC:.s=.o), 依赖关系再加上
%.o: %.s
        $(AS) -c $(ASFLAGS) $< -o $@
7. 编译, 提示找不到SystemInit函数, 再在main.c里加上空的SystemInit.
8. 编译成功, 下载到开发板, 还是不能运行, 直接进HardFault. 原因是没有指定指令集和MCU内核.
9. Makefile里CFLAGS变量加上-g -gdwarf-2 -mthumb -mcpu=cortex-m3, 再编译, 下载到开发板, 灯闪了.
10. for循环里i < 1000000UL改成100000UL, 去掉一个零, 再编译下载, 闪灯果然快了很多.

小弟们表示这个Makefile比网上各种例子里的简单多了.
再就是在目标里加了一行size, 编译完显示.text .data .bss字节数.

下次课预备先加上生成map和lst文件用于调试, 然后是命令行实现stlink烧写, 初始化时钟PLL, GPIO各种工作模式, 争取再讲点串口.

Franso 发表于 2023-4-1 00:38:34

tomzbj 发表于 2022-3-17 16:13
简单列个大纲,到时候边讲边完善吧

一.模拟电路
(引用自7楼)

小弟们学会MultiSIM了么{:biggrin:}

tomzbj 发表于 2023-4-1 10:52:42

Franso 发表于 2023-4-1 00:38
小弟们学会MultiSIM了么
(引用自111楼)

早就会了啊, 这还用教多久?

tomzbj 发表于 2023-4-8 22:15:25

本帖最后由 tomzbj 于 2023-4-8 22:29 编辑

4.7课程更新:

0. 在上次的基础上, Makefile前面加一行:STLINK := "C:/Program Files (x86)/STMicroelectronics/STM32 ST-LINK Utility/ST-LINK Utility/ST-LINK_CLI.exe"

后面加两行:flash:
        $(STLINK) -c SWD freq=4000 -P $(TARGET).hex -V -Rst
reset:
        $(STLINK) -c SWD -Rs
于是在命令行执行make flash和make reset就可以实现下载和复位了.
1. main.c, 把闪灯的部分单独拿出来做成一个函数gpio_toggle, 在main函数里调用, 再在这个函数里加个while(1); 于是程序运行到这里就卡死了.
2. 用stlink连接,MCU Core, 单步运行, PC寄存器的值一直不动, 说明死循环了.
3. 但是怎么从PC的值知道卡在哪里? 首先在Makefile里加一行:
LDFLAGS += -Wl,-Map=$(TARGET).map,--cref,--no-warn-mismatch

4. 再编译运行, 多了一个main.map, 在里面查找PC寄存器对应的地址, 于是可以知道是卡在哪个函数里了.
5. 在gpio_toggle函数里加个void (*func)(void), 让它指向NULL, 然后直接调用这个函数指针, 于是又卡死了.
6. 再用stlink连接, 检查PC寄存器, 再对照main.map, 这次卡在Default_Handler了. 这是因为没有明确给出HardFault_Handler, 而启动文件里有个弱符号HardFault_Handler是Default_Handler的别名.
7. main.c里再加一段:
void HardFault_Handler(void)
{
        while(1);
}

8. 再编译运行, stlink检查PC寄存器, 这次确定是卡在HardFault_Handler了. 但是怎么知道是从哪掉进HardFault的?
9. 查R13寄存器, 把里面的值复制到addr里, 读取几十字节, 再看第6或第7个32位值, 这是掉进HardFault之前的地址, 再对照main.map检查, 这次知道是在gpio_toggle函数里出错了.
10. 查map文件还是嫌太麻烦怎么办? 再在Makefile里给CFLAGS加一行 -Wa,-ahlms=$(<:.c=.lst), 编译运行, 这次又多了main.lst文件.
11. 卡住了, 用stlink知道了地址, 命令行执行arm-none-eabi-addr2line 地址 -e main.elf, 这次直接给了出错的文件名和行号.
12. 小弟们大概知道调试流程了. 最后在之前空白的SystemInit函数里加上配置时钟的内容:
RCC_DeInit();
    FLASH_SetLatency(FLASH_Latency_0);
    RCC_HSEConfig(RCC_HSE_ON);
    int ret = RCC_WaitForHSEStartUp();
    if(ret == 0x01) {
      FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
      FLASH_SetLatency(FLASH_Latency_2);
   
      RCC_HCLKConfig(RCC_SYSCLK_Div1);
      RCC_PCLK2Config(RCC_HCLK_Div1);
      RCC_PCLK1Config(RCC_HCLK_Div2);
      RCC_PLLConfig(RCC_PLLSource_HSE_Div2, RCC_PLLMul_9);
      RCC_PLLCmd(ENABLE);
      while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
      RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
      while(RCC_GetSYSCLKSource() != 0x08);
    }
13. 上面这个SystemInit相比system_stm32f10x.c里的版本相对简单明了一些.给他们讲清楚, 每一行起什么作用, 为什么网上各种例子是RCC_PLLSource_HSE_Div1而我们这个是Div2, 因为各种STM32开发板一般用8M晶振而我们这个是16M的, 因此需要预先把HSE时钟二分频再进PLL.
14. 编译运行, 这次LED闪烁频率果然比之前快多了.
15. 指导他们实现简单的延时函数, 顺便说明, 初始化阶段可以使用ms级延时, 进入主循环之后只允许使用us级延时. 调度器之类以后再讲. 参数加volatile则是为了避免被编译器给优化掉.
void _delay_us(volatile int nus)
{
    nus *= 7;
    while(nus > 0) {
      nus--;
    }
}

void _delay_ms(volatile int nms)
{
    while(nms > 0) {
      _delay_us(1000);
      nms--;
    }
}

页: 1 [2]
查看完整版本: 准备给几个小弟办个速成班,哪位有经验么 @@