Page 1 of 1

现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-02-21T13:54:54+00:00
by 擎天殿
现在中国PHP太难干了,我已经打算转行搞嵌入式了。

今天买了开发板。
以前也没了解过嵌入式开发,这一入手不知道怎么入门,就在网上查了查,看着很多人说是从一个开发板开始练手。好吧,我就买了一个开发板。
大概三天能运到吧。等到了再更新进度。

不知道不在中国的PHPer们是不是不会像我们这么卷...... 有在海外的朋友也可以分享一下你们的情况。

Re: 现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-02-23T13:42:19+00:00
by 擎天殿
开发板收到了 天空星 和 地阔星 各一个,排针明天再焊接吧。

usb转ttl 的下载器也收到了,但是这个的小设备就很奇怪了,先上一张图吧
Image

而按地阔星的接线说明
Image
Image

是需要一个5V的电源从ttl接入到地阔星上的5V上的,如图二中的红色线

于是问题就来了,图一中的5V接针已经被短接帽给堵住了,如何才能引出5V呢?
问了客服,但是TA仅是一个值班的,具体他也说不明白。得明天他们的技术上班了之后再问。

Re: 现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-02-24T03:53:24+00:00
by 擎天殿
问了淘宝上卖下载器的那家,他们的技术看样是也不知道怎么回事,可能就是抄了别人家的板抄过来,他们的商品说明里明明用红色的警示色写着“模块上的跳线帽不可移除”,但是它家的技术却说“如果非要用下载器来供电的话,那您就把跳线帽拔掉”... 这前后矛盾的说法让我感觉这家店不可靠了。
也劝大家不要在这家店买了,它家的店铺叫“telesky旗舰店”,店铺网站是 https://telesky.tmall.com

我的解决办法是在 vcc 那个脚上又焊上了一段导线,把vcc引出来后再留给杜邦线去接。如图
Image
这样先试试吧

地阔星的排针也焊好了,焊了一半的时候焊丝用没了,就用了一些锡块焊了一部份,感觉下来还是焊丝好用啊。
Image
Image
Image

下一步得去买点焊丝了,再把天空星焊起来

Re: 现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-02-27T14:03:28+00:00
by 擎天殿
先没管天空星那块开发板,这几天的精力主要放在了地阔星上。

不知不觉过去了这么多天了,终于把linux下的开发环境搞好了。

按照官网的方法的确是很快就完成了第一个试验(这里也许会有人跟我说应该用“实验”而不是“试验”,对也许对你来说仅仅是实践别人的程序,而对我来说却是试着去理解这个程序的过程,所以你去用你的“实验”就行,我是我,你是你),就是让开发板上的那个灯亮起来。

但对我来说这仅仅是半步,因为我日常都是工作在linux下的,windows仅仅跑在虚拟机里,我不想以后都依赖于虚拟机,所以我必须想办法把整个开发环境部署在linux上。
然而,简体中文圈里一说嵌入式就离不开keil的那个集成式开发环境(IDE),虽然那是arm出的软件,对于开发arm处理器的程序而言无疑它是很好用的,但是那货却只有windows版(这么说也不太公平,因为它有一个vscode的扩展,还有一个cloud版),但我却是希望用我习惯的编辑器vim来编辑的,所以那些我都用不上。

我在搜索时发现了意法半导体(ST)出了一系列的软件:
STM32CubeProgrammer 是把编译好的程序写进单片机的,支持 ST-Link、 J-Link/Flasher、UART (这个就是我用的usb转ttl模块要使用的模式)、USB 四种模式。
STM32CubeMX 是在一开始的创建整个代码工程的,它能导出成 Makefile 工程,这个工程就可以在linux上用make命令直接编译。这里要说明的是:它导出的是使用HAL库的,这个库对于商业开发来说可能很快捷,但对于像我这样的初学者来说就屏蔽掉了所有的学习底层的机会~~ 所以我现在还用不上它。
STM32CubeIDE 这也是一个集成开发环境,类似于keil的那个,可以使用STM32CubeMX专门为其导出的工程项目。

这貌似是解决了整个链条,但并不完全适用于我的情况。
对于我来说只解决了把编译好的程序写进单片机的过程,写程序和编译的过程还没解决。


编译器要使用 arm-none-eabi-gcc 我所使用的操作系统 archlinux 上可以直接安装,它在 extra 库里,所以运行

Code: Select all

sudo pacman -S arm-none-eabi-gcc
就能安装好了,然后运行

Code: Select all

sudo pacman -Ss arm-none-eabi-gcc
就能看到如下的输出

Code: Select all

extra/arm-none-eabi-gcc 14.2.0-1 (237.8 MiB 1.7 GiB) (Installed)
    The GNU Compiler Collection - cross compiler for ARM EABI (bare-metal) target
或者

Code: Select all

extra/arm-none-eabi-gcc 14.2.0-1 [installed]
    The GNU Compiler Collection - cross compiler for ARM EABI (bare-metal)
    target
大体的使用方式是

Code: Select all

arm-none-eabi-gcc -c main.c -o main.o

我现在用的是地阔星的开发板,LED接到了 PC13 上
Image
-->完整版的原理图可以通过下面的链接查看
https://pro.lceda.cn/editor#id=192ba5bf ... f633b59b3e
这个网址需要打开后放一会儿,才能显示地阔星的原理图。不要问我为什么,去问嘉立创。

所以要让开发板上的LED亮起来的话就得让 PC13 输出高电平,并且还得维持住。
于是我写了以下的 main.c

Code: Select all


/*
 * author               timeline.menu@gmail.com
 * changelog            
 *                      UTC+0800 2025-02-27 20:11:25 021150737 058 09 4
 *                        Create this file.
 *                        




*/









typedef unsigned int  uint32_t;
typedef unsigned short  uint16_t;


#define PERIPH_BASE           ((uint32_t)0x40000000) /*!< Peripheral base address in the alias region */
#define AHBPERIPH_BASE        (PERIPH_BASE + 0x20000)
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)


// /*RCC外设基地址*/
#define RCC_BASE           (AHBPERIPH_BASE + 0x1000)

/*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/
#define RCC_APB2ENR        *(unsigned int*)(RCC_BASE+0x18)


// GPIOC 基地址
#define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)

/* GPIOC寄存器地址,强制转换成指针 */
#define GPIOC_CRL           *(unsigned int*)(GPIOC_BASE+0x00)
#define GPIOC_CRH           *(unsigned int*)(GPIOC_BASE+0x04)
#define GPIOC_IDR           *(unsigned int*)(GPIOC_BASE+0x08)
#define GPIOC_ODR           *(unsigned int*)(GPIOC_BASE+0x0C)
#define GPIOC_BSRR          *(unsigned int*)(GPIOC_BASE+0x10)
#define GPIOC_BRR           *(unsigned int*)(GPIOC_BASE+0x14)
#define GPIOC_LCKR          *(unsigned int*)(GPIOC_BASE+0x18)









/*
 * 延时函数
 */
void delay ( volatile uint32_t count )
{
  while ( count -- ) 
  {
  }
}





int main ( void )
{


  // 开启GPIO的端口时钟
  // 开启GPIOC时钟
  RCC_APB2ENR |= ( 0x01 << 4 ) ;




  // -- 配置GPIO的模式 -- begin --


  // 13端口的CNF位,置0
  GPIOC_CRH &= ~( 0x03 << 22 ) ;
  // 3的二进制为11再取反为00
  // 查文档得知 13端口的CNF为 22 23 两个数据位

  // 13端口的MODE位,置0
  GPIOC_CRH &= ~( 0x03 << 20 ) ;
  // 查文档得知 13端口的MODE为 20 21 两个数据位


  // 设置为通用推挽输出模式
  //00:通用推挽输出模式
  //01:通用开漏输出模式
  //10:复用功能推挽输出模式
  //11:复用功能开漏输出模式
  GPIOC_CRH |= ( 0x00 << 22 ) ;

  // 设置为 50MHz
  //00:输入模式(复位后的状态)
  //01:输出模式,最大速度10MHz
  //10:输出模式,最大速度2MHz
  //11:输出模式,最大速度50MHz
  GPIOC_CRH |= ( 0x03 << 20 ) ;



  // -- 配置GPIO的模式 -- end --






  while(1)
  {

    // PC13输出高电平
    GPIOC_ODR |= ( 0x01 << 13 ) ;
        delay(1000000);  // 延时


    // PC13输出低电平
    GPIOC_ODR &= ~( 0x01 << 13 ) ;
        delay(1000000);  // 延时
        delay(1000000);  // 延时

  }



  return 0 ;


}







单片机不同于纯软件的编程,还需要两个文件才能为实体硬件生成可执行的程序 一个启动文件和一个链接文件。
启动文件是一个汇编文件,stm32芯片启动流程如下:
1、用代码段的第1个32位数初始化堆栈指针SP
2、用代码段的第2个32位数初始化程序指针PC(也就是程序跳转到第2个32位数的数值指向的地址)
我们需要定义一个g_pfnVectors向量表,向量表中的 第一个值为SP初始化值, 第二个值为复位Reset_Handler函数地址。
在复位函数Reset_Handler中跳转到了mian函数。
为此我们可以这样写这个汇编文件,比如我们命名为 startup_stm32f10x_md.s

Code: Select all

/*
 * author               timeline.menu@gmail.com
 * changelog            
 *                      UTC+0800 2025-02-27 21:34:28 530777452 058 09 4
 *                        Create this file.
 *                        




*/







.syntax unified
.cpu cortex-m3
.fpu softvfp
.thumb



/******************************************************************************
复位启动函数Reset_Handler 执行跳转到main函数
******************************************************************************/ 
    .section	.text.Reset_Handler
	.weak	Reset_Handler
	.type	Reset_Handler, %function
Reset_Handler:	

	bl	main
	bx	lr    
.size	Reset_Handler, .-Reset_Handler



/******************************************************************************
向量表 第一个值为SP 第二个值为复位地址
******************************************************************************/ 
.global	g_pfnVectors  
 	.section	.isr_vector,"a",%progbits
	.type	g_pfnVectors, %object
	.size	g_pfnVectors, .-g_pfnVectors
    
    
g_pfnVectors:
	.word	_estack
	.word	Reset_Handler
	





再来就是链接文件了,它定义了代码应该下载到stm32f103rb芯片的什么位置。
链接可以将多个目标代码整合成最终的可执行程序,在链接过程中有个重要的步骤就是给程序指定一个起始位置。
因此我们编写一个简单的链接文件:比如我们命名为 stm32_flash.ld

Code: Select all

/*
 * author               timeline.menu@gmail.com
 * changelog            
 *                      UTC+0800 2025-02-27 21:38:35 969741863 058 09 4
 *                        Create this file.
 *                        




*/







/* 定义flash 和 ram 区域  */

MEMORY
{
    flash (rx) : ORIGIN = 0x08000000, LENGTH = 64K
    ram  (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}




ENTRY(Reset_Handler)


_heap_size = 0;    /* required amount of heap  */
_stack_size = 0;   /* required amount of stack */

/* The stack starts at the end of RAM and grows downwards. Full-descending*/
_estack = ORIGIN(ram) + LENGTH(ram);

SECTIONS
{
    /* Reset and ISR vectors */
    .isr_vector :
    {
        __isr_vector_start__ = .;
        KEEP(*(.isr_vector)) /* without 'KEEP' the garbage collector discards this section */
        ASSERT(. != __isr_vector_start__, "The .isr_vector section is empty");
    } >flash


    /* Text section (code and read-only data) */
    .text :
    {
        . = ALIGN(4);
        _stext = .;
        *(.text*)   /* code */
        *(.rodata*) /* read only data */

        /*
         * NOTE: .glue_7 and .glue_7t sections are not needed because Cortex-M
         * only supports Thumb instructions, no ARM/Thumb interworking.
         */

        /* Static constructors and destructors */
        KEEP(*(.init))
        KEEP(*(.fini))

        . = ALIGN(4);
        _etext = .;
    } >flash


    /*
     * Initialized data section. This section is programmed into FLASH (LMA
     * address) and copied to RAM (VMA address) in startup code.
     */
    _sidata = .;
    .data : AT(_sidata) /* LMA address is _sidata (in FLASH) */
    {
        . = ALIGN(4);
        _sdata = .; /* data section VMA address */
        *(.data*)
        . = ALIGN(4);
        _edata = .;
    } >ram


    /* Uninitialized data section (zeroed out by startup code) */
    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = .;
    } >ram


    /*
     * Reserve memory for heap and stack. The linker will issue an error if
     * there is not enough memory.
     */
    ._heap :
    {
        . = ALIGN(4);
        . = . + _heap_size;
        . = ALIGN(4);
    } >ram
    ._stack :
    {
        . = ALIGN(4);
        . = . + _stack_size;
        . = ALIGN(4);
    } >ram
}

/* Nice to have */
__isr_vector_size__ = SIZEOF(.isr_vector);
__text_size__ = SIZEOF(.text);
__data_size__ = SIZEOF(.data);
__bss_size__ = SIZEOF(.bss);






基础的代码都有了,下面就是编译规则了,当然写成shell也行,但大多数公司都喜欢用 makefile ,它的方便性就是可以一个make命令就解决(貌似这点shell也可以办到~~,但makefile就是这么宣传的~~)
我写的Makefile如下

Code: Select all

#
 # author               timeline.menu@gmail.com
 # changelog            
 #                      UTC+0800 2025-02-27 21:44:38 496632456 058 09 4
 #                       Create this file.
 #                       




#




# toolchain
CC = arm-none-eabi-gcc
CP = arm-none-eabi-objcopy
AS = arm-none-eabi-gcc -x assembler-with-cpp



# all the files will be generated with this name 
PROJECT_NAME = stm32f10x_project
# 这个是生成的文件的名字,需要修改的话就在这里改



# user specific
SRC += ./main.c
# 如果有其他源文件可以附在后面,以半角空格分隔



# startup
ASM_SRC  += ./startup_stm32f10x_md.s



OBJECTS  = $(ASM_SRC:.s=.o) $(SRC:.c=.o)



# Define optimisation level here
MC_FLAGS = -mcpu=cortex-m3
AS_FLAGS = $(MC_FLAGS) -g  -mthumb  
CP_FLAGS = $(MC_FLAGS)  -g  -mthumb  -Wall -fverbose-asm 
LD_FLAGS = $(MC_FLAGS) -g  -mthumb  -Xlinker --gc-sections -T stm32_flash.ld 





# build
all: $(OBJECTS) $(PROJECT_NAME).elf  $(PROJECT_NAME).hex $(PROJECT_NAME).bin
	 arm-none-eabi-size $(PROJECT_NAME).elf

%.o: %.c
	$(CC) -c $(CP_FLAGS) -I . $(INC_DIR) $< -o $@

%.o: %.s
	$(AS) -c $(AS_FLAGS) $< -o $@

%.elf: $(OBJECTS)
	$(CC) $(OBJECTS) $(LD_FLAGS) -o $@

%.hex: %.elf
	$(CP) -O ihex $< $@

%.bin: %.elf
	$(CP) -O binary -S  $< $@




# clean
clean:
	rm -v  $(PROJECT_NAME).elf $(PROJECT_NAME).hex $(PROJECT_NAME).bin *.o









这样一个基本的程序就完成了。
在这个路径下可以通过执行

Code: Select all

make
就可以生成了,执行后就可以在路径下看到生成的一堆 .o 文件,还有 stm32f10x_project.elf stm32f10x_project.bin stm32f10x_project.hex 这几个文件,其中 stm32f10x_project.hex 就是我要写进单片机里的程序。

这时就可以让STM32CubeProgrammer出场了,具体的操作方式可以参考 https://wiki.lckfb.com/zh-hans/dkx-stm3 ... B%E5%BA%8F
需要注意的是在linux下起动这个程序需要给予权限,这与在windows下不同。
我是用的

Code: Select all

sudo ~/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32CubeProgrammer
来起动它的。
选择UART后我的ttl模块是以 /dev/ttyUSB0 的名称出现的,也许你的会有所不同,请根据你的实际情况修改。

“烧录”完成后,终于看到了那个小蓝灯在闪了,频率就是 main.c 里面的延时所定义的。

消化消化后,下一步要开始玩ST的标准库了。
之后会再更新。

Re: 现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-03-04T01:57:07+00:00
by 擎天殿
买的焊丝到了
Image
这次买的99%的,熔点有一点点高,但是焊出来的效果挺好。
这是焊好的天空星
Image
请忽略上面的那一排焊油渍,那是我习惯了先上焊油再上锡,但是用这个焊丝就不用再单独上焊油了,因为它里面本身就含有2.2%的焊油。

Re: 现在中国PHP太难干了,我已经打算转行搞嵌入式了

Posted: 2025-03-04T08:54:15+00:00
by 擎天殿
用stm32标准库的方式也让板载的LED闪起来了


嵌入式很讲究细节啊