0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心
发布

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM32单片机到底如何软硬件结合

GReq_mcu168 来源:STM32嵌入式开发 作者:屋脊雀工作室 2022-05-17 15:04 次阅读
不知道大家有没有疑惑,为什么软件能控制硬件? 本文分析STM32单片机到底是如何硬件结合的,分析单片机程序如何编译,运行。

软硬件结合

初学者,通常有一个困惑,就是为什么软件能控制硬件?就像当年的51,为什么只要写P1=0X55,就可以在IO口输出高低电平?要理清这个问题,先要认识一个概念:地址空间

寻址空间

什么是地址空间呢?所谓的地址空间,就是PC指针的寻址范围,因此也叫寻址空间。
大家应该都知道,我们的电脑有32位系统和64位系统之分,为什么呢?因为32位系统,PC指针就是一个32位的二进制数,也就是0xffffffff,范围只有4G寻址空间。现在内存越来越大,4G根本不够,所以需要扩展,为了能访问超出4G范围的内存,就有了64位系统。STM32是多少位的?是32位的,因此PC指针也是32位,寻址空间也就是4G。
我们来看看STM32的寻址空间是怎么样的。在数据手册《STM32F407_数据手册.pdf》中有一个图,这个图,就是STM32的寻址空间分配。所有的芯片,都会有这个图,名字基本上都是叫Memory map,用一个新芯片,就先看这个图。 036e53fa-d5a9-11ec-bce3-dac502259ad0.png
  • 最左边,8个block,每个block 512M,总共就是4G,也就是芯片的寻址空间。

  • block 0 里面有一段叫做FLASH,也就是内部FLASH,我们的程序就是下载到这个地方,起始地址是0X800 0000,大家注意,这个只有1M空间。现在STM32已经有2M flash的芯片了,超出1M的FLASH放在哪里呢?请自行查看对应的芯片手册。

  • 3 在block 1 内,有两段SRAM,总共128K,这个空间,也就是我们前面说的内存,存放程序使用的变量。如果需要,也可以把程序放到SRAM中运行。407不是有196K吗?

  • 其实407有196K内存,但是有64k并不是普通的SRAM,而是放在block 0 内的CCM。这两段区域不连续,而且,CCM只能内核使用,外设不能使用,例如DMA就不能用CCM内存,否则就死机。

  • block 2,是Peripherals,也就是外设空间。我们看右边,主要就是APB1/APB2、AHB1/AHB2,什么东西呢?回头再说。

  • block 3、block4、block5,是FSMC的空间,FSMC可以外扩SRAM,NAND FALSH,LCD等外设。

好的,我们分析了寻址空间,我们回过头看看,软件是如何控制硬件的。对于这个疑惑,也可以看此文:代码是如何控制硬件的?在IO口输出的例程中,我们配置IO口是调用库函数,我们看看库函数是怎么做的。 例如:

        GPIO_SetBits(GPIOG, GPIO_Pin_0| GPIO_Pin_1 |GPIO_Pin_2| GPIO_Pin_3);
        这个函数其实就是对一个变量赋值,对GPIOx这个结构体的成员BSRRL赋值。
        

        voidGPIO_SetBits(GPIO_TypeDef* GPIOx,uint16_tGPIO_Pin)
        {
        /* Check the parameters*/
        assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
        assert_param(IS_GPIO_PIN(GPIO_Pin));
        GPIOx->BSRRL = GPIO_Pin;
        }
        
assert_param:这个是断言,用于判断输入参数是否符合要求GPIOx是一个输入参数,是一个GPIO_TypeDef结构体指针,所以,要用->获取其成员
GPIOx是我们传入的参数GPIOG,具体是啥?在stm32f4xx.h中有定义。

        #defineGPIOG ((GPIO_TypeDef *) GPIOG_BASE)
        GPIOG_BASE同样在文件中有定义,如下:
        

        #defineGPIOG_BASE (AHB1PERIPH_BASE + 0x1800)
        AHB1PERIPH_BASE,AHB1地址,有点眉目了吧?在进一步看看
        

        /*!< Peripheral memory map */
        #defineAPB1PERIPH_BASE PERIPH_BASE
        #defineAPB2PERIPH_BASE (PERIPH_BASE + 0x00010000)
        #defineAHB1PERIPH_BASE (PERIPH_BASE + 0x00020000)
        #defineAHB2PERIPH_BASE (PERIPH_BASE + 0x10000000)
        再找找PERIPH_BASE的定义
        

        #definePERIPH_BASE ((uint32_t)0x40000000)
        到这里,我们可以看出,操作IO口G,其实就是操作0X40000000+0X1800这个地址上的一个结构体里面的成员。说白了,就是操作了这个地方的寄存器。实质跟我们操作普通变量一样,就像下面的两句代码,区别就是变量i是SRAM空间地址,0X40000000+0X1800是外设空间地址。
        

        u32 i;
        i = 0x55aa55aa;
        这个外设空间地址的寄存器是IO口硬件的一部分。关于如下图STM32的GPIO文章推荐
        STM32中GPIO工作原理详解
        。如下图,左边的输出数据寄存器,就是我们操作的寄存器(内存、变量),它的地址就是0X40000000+0X1800+0x14.
        03b6ef70-d5a9-11ec-bce3-dac502259ad0.png
        控制其他外设也类似,就是将数据写到外设寄存器上,跟操作内存一样,就可控制外设了。
        
寄存器,其实应该是内存的统称,外设寄存器应该叫做特殊寄存器。慢慢的,所有人都把外设的叫做寄存器,其他的统称内存或RAM。寄存器为什么能控制硬件外设呢?因为,初略的说,一个寄存器的一个BIT,就是一个开关,开就是1,关就是0。通过这个电子开关去控制电路,从而控制外设硬件。

纯软件-包罗万象的小程序

我们已经完成了串口和IO口的控制,但是我们仅仅知道了怎么用,对其他一无所知。程序怎么跑的?关于程序是怎么在单片机运行的,也可以看此视频 动画演示单片机是如何跑程序的 。代码到底放在那里?内存又是怎么保存的?下面,我们通过一个简单的程序,学习嵌入式软件的基本要素。

分析启动代码

  • 函数从哪里开始运行?

每个芯片都有复位功能,复位后,芯片的PC指针(一个寄存器,指示程序运行位置,对于多级流水线的芯片,PC可能跟真正执行的指令位置不一致,这里暂且认为一致)会复位到固定值,一般是0x00000000,在STM32中,复位到0X08000004。因此复位后运行的第一条代码就是0X08000004。前面我们不是拷贝了一个启动代码文件到工程吗?startup_stm32f40_41xxx.s,这个汇编文件为什么叫启动代码?因为里面的汇编程序,就是复位之后执行的程序。在文件中,有一段数据表,称为中断向量,里面保存了各个中断的执行地址复位,也是一个中断。 芯片复位时,芯片从中断表中将Reset_Handler这个值(函数指针)加载到PC指针,芯片就会执行Reset_Handler函数了。(一个函数入口就是一个指针

        ; Vector Table Mapped toAddress 0 atReset
        AREARESET,DATA, READONLY
        EXPORT__Vectors
        EXPORT__Vectors_End
        EXPORT__Vectors_Size
        __VectorsDCD __initial_sp ; Top of Stack
        DCD Reset_Handler ;ResetHandler
        DCD NMI_Handler ; NMIHandler
        DCD HardFault_Handler ; Hard FaultHandler
        DCD MemManage_Handler ; MPU FaultHandler
        DCD BusFault_Handler ; Bus FaultHandler
        DCD UsageFault_Handler ; Usage FaultHandler
        Reset_Handler函数,先执行SystemInit函数,这个函数在标准库内,主要是初始芯片时钟。然后跳到__main执行,__main函数是什么函数?
        是我们在main.c中定义的main函数吗?后面我们再说这个问题。
        03c86df4-d5a9-11ec-bce3-dac502259ad0.png
        芯片是怎么知道开始就执行启动代码的呢?或者说,我们如何把这个启动代码放到复位的位置?这就牵涉到一个一般情况下不关注的文件wujique.sct,这个文件在wujiqueprjObjects目录下,通常把这个文件叫做分散加载文件,编译工具在链接时,根据这个文件放置各个代码段和变量。
        在MDK软件Options菜单Linker下有关于这个菜单的设置。
        03d54696-d5a9-11ec-bce3-dac502259ad0.png
        把Use Memory Layout from TargetDialog前面的勾去掉,之前不可设置的框都可以设置了。点击Edit进行编辑。
        03ed01aa-d5a9-11ec-bce3-dac502259ad0.png
        在代码编辑框出现了分散加载文件内容,当前文件只有基本的内容。
        
其实这个文件功能很强大,通过修改这个文件可以配置程序的很多功能,例如:1 指定FLASH跟RAM的大小于起始位置,当我们把程序分成BOOT、CORE、APP,甚至进行驱动分离的时候,就可以用上了。2 指定函数与变量的位置,例如把函数加载到RAM中运行。
0421b27e-d5a9-11ec-bce3-dac502259ad0.png 从这个基本的分散加载文件我们可以看出:
  • 第6行 ER_IROM1 0x08000000 0x00080000定义了ER_IROM1,也就是我们说的内部FLASH,从0x08000000开始,大小0x00080000。

  • 第7行.o (RESET, +First)从0x08000000开始,先放置一个.o文件, 并且用(RESET, +First)指定RESET块优先放置,RESET块是什么?请查看启动代码,中断向量就是一个AREA,名字叫RESET,属于READONLY。这样编译后,RESET块将放在0x08000000位置,也就是说,中断向量就放在这个地方。DCD是分配空间,4字节,第一个就是__initial_sp,第二个就是Reset_Handler函数指针。也就是说,最后编译后的程序,将Reset_Handler这个函数的指针(地址),放在0x800000+4的地方。所以芯片在复位的时候,就能找到复位函数Reset_Handler。

  • 第8行 *(InRoot$$Sections)什么鬼?GOOGLE啊!回头再说。

  • 第9行 .ANY (+RO)意思就是其他的所有RO,顺序往后放。就是说,其他代码,跟着启动代码后面。

  • 第11行 RW_IRAM1 0x20000000 0x00020000定义了RAM大小。

  • 第12行 .ANY (+RW +ZI)所有的RW ZI,全部放到RAM里面。RW,ZI,也就是变量,这一行指定了变量保存到什么地址。

分析用户代码

到此,基本启动过程已经分析完。下一步开始分析用户代码,就从main函数开始。 1 程序跳转到main函数后:RCC_GetClocksFreq获取RCC时钟频率;SysTick_Config配置SysTick,在这里打开了SysTick中断,10毫秒一次。Delay(5);延时50毫秒。

        intmain(void)
        {
        GPIO_InitTypeDef GPIO_InitStructure;
        /*!< At this stage the microcontroller clock setting is already configured,
        this is done through SystemInit() function which is calledfrom startup
        files before to branch to application main.
        To reconfigure the default setting of SystemInit() function,
        refer to system_stm32f4xx.c file */
        /* SysTick end of count event each 10ms */
        RCC_GetClocksFreq(&RCC_Clocks);
        SysTick_Config(RCC_Clocks.HCLK_Frequency /100);
        /* Add your application code here */
        /* Insert 50 ms delay */
        Delay(5);
        2 初始化IO就不说了,进入while(1),也就是一个死循环,嵌入式程序,都是一个死循环,否则就跑飞了。
        

        /*初始化LED IO口*/
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2| GPIO_Pin_3;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOG, &GPIO_InitStructure);
        /* Infiniteloop */
        mcu_uart_open(3);
        while(1)
        {
        GPIO_ResetBits(GPIOG, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
        Delay(100);
        GPIO_SetBits(GPIOG, GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);
        Delay(100);
        mcu_uart_test();
        TestFun(TestTmp2);
        }
        3 在while(1)中调用TestFun函数,这个函数使用两个全局变量,两个局部变量。
        

        /* Private functions ---------------------------------------------------------*/
        u32 TestTmp1 =5;//全局变量,初始化为5
        u32 TestTmp2;//全局变量,未初始化
        constu32 TestTmp3[10] = {6,7,8,9,10,11,12,13,12,13};
        u8TestFun(u32 x)//函数,带一个参数,并返回一个u8值
        {
        u8 test_tmp1 =4;//局部变量,初始化
        u8 test_tmp2;//局部变量,未初始化
        staticu8 test_tmp3 =0;//静态局部变量
        test_tmp3++;
        test_tmp2 = x;
        if(test_tmp2> TestTmp1)
        test_tmp1 =10;
        else
        test_tmp1 =5;
        TestTmp2 +=TestTmp3[test_tmp1];
        returntest_tmp1;
        }
        然后程序就一直在main函数的while循环里面执行。中断呢?对,还有中断。中断中断,就是中断正常的程序执行流程。相关文章:
        STM32中断系统
        。我们查看Delay函数,uwTimingDelay不等于0就死等?谁会将uwTimingDelay改为0?
        

        /**
        *@briefInserts a delay time.
        *@paramnTime: specifies the delay time length, in milliseconds.
        *@retvalNone
        */
        voidDelay(__IO uint32_t nTime)
        {
        uwTimingDelay = nTime;
        while(uwTimingDelay !=0);
        }
        搜索uwTimingDelay变量,函数TimingDelay_Decrement会将变量一直减到0。
        

        /**
        *@briefDecrements the TimingDelay variable.
        *@paramNone
        *@retvalNone
        */
        voidTimingDelay_Decrement(void)
        {
        if(uwTimingDelay !=0x00)
        {
        uwTimingDelay--;
        }
        }
        这个函数在哪里执行?经查找,在SysTick_Handler函数中运行。谁用这个函数?
        

        /**
        *@briefThis function handles SysTick Handler.
        *@paramNone
        *@retvalNone
        */
        voidSysTick_Handler(void)
        {
        TimingDelay_Decrement();
        }
        经查找,在中断向量表中有这个函数,也即是说这个函数指针保存在中断向量表内。当发生中断时,就会执行这个函数。当然,在进出中断会有保存和恢复现场的操作。这个主要涉及到汇编,暂时不进行分析了。有兴趣自己研究研究。通常,现在我们开发程序不用关心上下文切换了。
        

        __Vectors DCD __initial_sp ; Top of Stack
        DCD Reset_Handler ;ResetHandler
        DCD NMI_Handler ; NMIHandler
        DCD HardFault_Handler ; Hard FaultHandler
        DCD MemManage_Handler ; MPU FaultHandler
        DCD BusFault_Handler ; Bus FaultHandler
        DCD UsageFault_Handler ; Usage FaultHandler
        DCD0; Reserved
        DCD 0 ; Reserved
        DCD 0 ; Reserved
        DCD 0 ; Reserved
        DCD SVC_Handler ; SVCallHandler
        DCD DebugMon_Handler ; Debug MonitorHandler
        DCD0; Reserved
        DCD PendSV_Handler ; PendSVHandler
        DCD SysTick_Handler ; SysTickHandler
        

余下问题

1 __main函数是什么函数?是我们在main.c中定义的main函数吗?2 分散加载文件中*(InRoot$$Sections)是什么?3 ZI段,也就是初始化为0的数据段,什么时候初始化?谁初始化? 为什么这几个问题前面留着不说?因为这是同一个问题。顺藤摸瓜!

通过MAP文件了解代码构成

编译结果

程序编译后,在下方的Build Output窗口会输出 信息

        ***Using Compiler 'V5.06 update 5 (build 528)', folder: 'C:Keil_v5ARMARMCCBin'
        Buildtarget 'wujique'
        compilingstm32f4xx_it.c...
        ...
        assemblingstartup_stm32f40_41xxx.s...
        compilingmisc.c...
        ...
        compilingmcu_uart.c...
        linking...
        ProgramSize: Code=9038 RO-data=990 RW-data=40 ZI-data=6000
        FromELF:creating hex file...
        ".Objectswujique.axf"- 0 Error(s), 0 Warning(s).
        BuildTime Elapsed: 0032
        
  • 编译目标是wujique
  • C文件compiling,汇编文件assembling,这个过程叫编译
  • 编译结束后,就进行link,链接。
  • 最后得到一个编译结果,9038字节code,RO 990,RW 40,ZI 6000。CODE,是代码,很好理解,那RO、RW、ZI都是什么?
  • FromELF,创建hex文件,FromELF是一个好工具,需要自己添加到option中才能用

map文件配置

更多编译具体信息在map文件中,在MDK Options中我们可以看到,所有信息都放在Listingswujique.map
默认很多编译信息可能没钩,钩上所有信息会增加编译时间。
0433f1c8-d5a9-11ec-bce3-dac502259ad0.png

map文件

打开map文件,好乱?习惯就好。我们抓重点就行了。 0446ef3a-d5a9-11ec-bce3-dac502259ad0.png
  • map 总信息

从最后看起,看到没?最后的这一段map内容,说明了整个程序的基本概况。 有多少RO?RO到底是什么? 有多少RW?RW又是什么? ROM为什么不包括ZI Data?为什么包含RW Data? 0459ac7e-d5a9-11ec-bce3-dac502259ad0.png
  • Image component sizes

往上,看看Image component sizes,这个就比刚刚的总体统计更细了。 这部分内容,说明了每个源文件的概况 首先,是我们自己的源码,这个程序我们的代码不多,只有main.o,wujique_log.o,和其他一些STM32的库文件。 048f38f8-d5a9-11ec-bce3-dac502259ad0.png 第2部分是库里面的文件,看到没?里面有一个main.o。main函数是不是我们写的main函数?明显不是,我们的main函数是放在main.o文件。这么小的一个工程,用了这么多库,你以前关注过吗?估计没有,除非你曾经将一个原本在1M flash上的程序压缩到能在512K上运行。 04a39852-d5a9-11ec-bce3-dac502259ad0.png 第3部分也是库,暂时没去分析这两个是什么东西。 04d0b120-d5a9-11ec-bce3-dac502259ad0.png
库文件是什么?库文件就是别人已经别写好的代码库。在代码中,我们经常会包含一些头文件,例如:

         #include
           #include
           #include
           
         这些就是库的头文件。这些头文件保存在MDK开发工具的安装目录下。我们经常用的库函数有:memcpy、memcmp、strcmp等。只要代码中包含了这些函数,就会链接库文件。
        
  • 文件map

再往上,就是文件MAP了,也就时每个文件中的代码段(函数)跟变量在ROM跟RAM中的位置。首先是ROM在0x08000000确实放的是startup_stm32f40_41xxx.o中的RESET
库文件是什么?
库文件就是别人已经别写好的代码库。 在代码中,我们经常会包含一些头文件,例如:

        #include
           
        #include
           
        #include
           
        这些就是库的头文件。相关文章:
        C语言中的头文件
        。这些头文件保存在MDK开发工具的安装目录下。
        我们经常用的库函数有:memcpy、memcmp、strcmp等。
        只要代码中包含了这些函数,就会链接库文件。
        
  • 文件map
再往上,就是文件MAP了,也就时每个文件中的代码段(函数)跟变量在ROM跟RAM中的位置。首先是ROM在0x08000000确实放的是startup_stm32f40_41xxx.o中的RESET 0501280a-d5a9-11ec-bce3-dac502259ad0.png 每个文件有有多行,例如串口,4个函数。 051033c2-d5a9-11ec-bce3-dac502259ad0.png 然后是RAM的,main.o中的变量,放在0x20000000,总共有0x0000000c,类型是Data、RW。串口有两种变量,data和bss,什么是bss?这两个名称,是section name,也就是段的意思。看前面type和Attr, RW Data,放在.data段;RW Zero放在.bss段,RW Zero,其实就是ZI。到底哪些变量是RW,哪些是ZI? 052fc4bc-d5a9-11ec-bce3-dac502259ad0.png
  • Image Symbol Table

再往上就是Image Symbol Table,就更进一步到每个函数或者变量的信息了 056647c6-d5a9-11ec-bce3-dac502259ad0.png 例如,全局变量TestTmp1,是Data,4字节,分配的位置是0x20000004。 0593906e-d5a9-11ec-bce3-dac502259ad0.png TestTmp3数组放在哪里?放在0X080024E0这个地方,这可是代码区额。因为我们用const修饰了这个全局变量数组,告诉编译器,这个数组是不可以改变的,编译器就将这个数组保存到代码中了。程序中我们经常会使用一些大数组数据,例如字符点阵,通常有几K几十K大,不可能也没必要放到RAM区,整个程序运行过程这些数据都不改变,因此通过const修饰,将其存放到代码区。
const的用处比较多,可以修饰变量,也可以修饰函数。更多用法自行学习
05b68498-d5a9-11ec-bce3-dac502259ad0.png 那局部变量存放在哪里呢?我们找到了test_tmp3, 05c7606a-d5a9-11ec-bce3-dac502259ad0.png 没找到test_tmp1/test_tmp2,为什么呢?在定义时,test_tmp3增加了static定义,意思就是静态局部变量,功能上,相当于全局变量,定义在函数内,限制了这个全局变量只能在这个函数内使用。哪test_tmp1、test_tmp2放在哪里呢?局部变量,在编译链接时,并没有分配空间,只有在运行时,才从栈分配空间。

        u8TestFun(u32 x)//函数,带一个参数,并返回一个u8值
        {
        u8 test_tmp1 =4;//局部变量,初始化
        u8 test_tmp2;//局部变量,未初始化
        staticu8 test_tmp3 =0;//静态局部变量
        上一部分,我们留了一个问题,哪些变量是RW,哪些是ZI?我们看看串口变量的情况,UartBuf3放在bss段,其他变量放在.data段。为什么数组就放在bss?bss是英文Block Started by Symbol的简称。
        05dd085c-d5a9-11ec-bce3-dac502259ad0.png
        到这里,我们可解释下面几个概念了:
        
Code就是代码,函数。 RO Data,就是只读变量,例如用const修饰的数组。 RW Data,就是读写变量,例如全局变量跟static修饰的局部变量。 ZI Data,就是系统自动初始化为0的读写变量,大部分是数组,放在bss段。 RO Size等于代码加只读变量。 RW Size等于读写变量(包括自动初始化为0的),这个也就是RAM的大小。 ROM Size,也就是我们编译之后的目标文件大小,也就是FLASH的大小。但是?为什么会包含RW Data呢?因为所有全局变量都需要一个初始化的值(就算没有真正初始化,系统也会分配一个初始化空间),例如我们定义一个变量u8 i = 8;这样的全局变量,8,这个值,就需要保存在FALSH区。
06091afa-d5a9-11ec-bce3-dac502259ad0.png 我们看看函数的情况,前面我们不是有一个问题吗?__main和main是一个函数吗?查找main后发现,main是main,放在0x08000579 0617d45a-d5a9-11ec-bce3-dac502259ad0.png main是main,放在0x08000189 0629b56c-d5a9-11ec-bce3-dac502259ad0.png __main到main之间发生了什么?还记得分散加载文件中的这句吗?

        *(InRoot$$Sections)
        __main就在这个段内。下图是__main的地址,在0x08000189。__Vectors就是中断向量,放在最开始。
        063d6cba-d5a9-11ec-bce3-dac502259ad0.png
        在分散加载文件中,紧跟RESET的就是*(InRoot$$Sections)。
        064c13dc-d5a9-11ec-bce3-dac502259ad0.png
        而且,RESET段正好大小0x00000188。
        06829394-d5a9-11ec-bce3-dac502259ad0.png
        巧合?可以参考PPT文档《ARM嵌入式软件开发.ppt》。
        06928e66-d5a9-11ec-bce3-dac502259ad0.png
        这一段代码都完成什么功能呢?主要完成ZI代码的初始化,也就是将一部分RAM初始化为0。其他环境初始化……
        

最后

到这里,一个程序,是怎么组成的,程序是如何运行的,基本有一个总体印象了。

原文标题:STM32是如何软硬件结合,编译后怎么样一步步运行起来的

文章出处:【微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6017

    文章

    44267

    浏览量

    626569
  • STM32
    +关注

    关注

    2253

    文章

    10761

    浏览量

    351516
  • 软硬件
    +关注

    关注

    1

    文章

    280

    浏览量

    19124

原文标题:STM32是如何软硬件结合,编译后怎么样一步步运行起来的

文章出处:【微信号:mcu168,微信公众号:硬件攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    单片机是怎么做到软硬件结合

    我们通过IO和串口的软件开发,已经体验了嵌入式软件开发。不知道大家有没有疑惑,为什么软件能控制 硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析 单片机 到底是如何 软硬件
    的头像 发表于01-17 15:25 4072次阅读
    <b class='flag-5'>单片机</b>是怎么做到<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的

    STM32单片机到底是如何实现软硬件结合

    本文分析 STM32 单片机 到底是如何实现 软硬件 结合的,接着分析 单片机程序如何编译、运行。
    发表于05-16 09:54 951次阅读
    <b class='flag-5'>STM32</b><b class='flag-5'>单片机</b><b class='flag-5'>到底</b>是如何实现<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>?

    单片机到底是如何软硬件结合

    不知道大家有没有疑惑,为什么软件能控制 硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析 单片机 到底是如何 软硬件 结合
    的头像 发表于08-08 09:31 1159次阅读
    <b class='flag-5'>单片机</b><b class='flag-5'>到底</b>是如何<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的

    AVR单片机软硬件设计教程入门篇

    AVR 单片机 软硬件设计教程入门篇 单片机就是一台微型电脑。麻雀虽小,五脏俱全, 单片机将微型电脑的所有基本部分都集成在一片硅片上。构成 单片机的基
    发表于12-09 15:22

    承接单片机软硬件开发

    领先 单片机工作室在广州,专业承接控制类,消费类电子产品 软硬件开发业务,包括51,PIC,AVR,三星,EMC等8位 单片机,MSP430等16位 单片机以及LPC,
    发表于04-04 14:16

    单片机按键软硬件设计技巧有哪些

    单片机按键 软硬件设计技巧!
    发表于04-22 14:37

    为什么软件能控制硬件单片机到底是如何软硬件结合

    我们通过IO和串口的软件开发,已经体验了嵌入式软件开发。不知道大家有没有疑惑,为什么软件能控制 硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析 单片机 到底是如何 软硬件
    发表于02-14 06:22

    单片机测控系统的软硬件平台技术

    本文探讨了一种用于工业测控系统的 单片机 软硬件综合设计方法—— 软硬件平台技术,重点阐述了其基本原理、设计思想、实现方法,并给出了一个 单片机测控系统 软硬
    发表于08-13 09:38 12次下载

    AVR单片机软硬件学习教程

    AVR 单片机 软硬件学习教程 o第一讲: 单片机及其开发工具简介 o第二讲:AVR 硬件电路设计教程 o第三讲:AVR开发环境的建立,数字逻辑与C语言程序设计基础知识 o第四讲:流水灯、蜂鸣
    发表于11-30 15:36 524次下载
    AVR<b class='flag-5'>单片机</b><b class='flag-5'>软硬件</b>学习教程

    单片机系统软硬件调试及维修技术

    单片机系统 软硬件调试及维修技术,快来下载吧
    发表于09-01 18:17 0次下载

    深度解剖单片机是如何软硬件结合的并分析单片机程序的编译,运行

    我们通过IO和串口的软件开发,已经体验了嵌入式软件开发。不知道大家有没有疑惑,为什么软件能控制 硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析 单片机 到底是如何 软硬件
    的头像 发表于04-03 11:46 1749次阅读
    深度解剖<b class='flag-5'>单片机</b>是如何<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的并分析<b class='flag-5'>单片机</b>程序的编译,运行

    深度:单片机到底是如何软硬件结合的?

    我们通过IO和串口的软件开发,已经体验了嵌入式软件开发。不知道大家有没有疑惑,为什么软件能控制 硬件?反正当年我学习51的时候,有这个疑惑。今天我们就暂停软件开发,分析 单片机 到底是如何 软硬件
    发表于12-09 10:51 0次下载
    深度:<b class='flag-5'>单片机</b><b class='flag-5'>到底</b>是如何<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的?

    专业软硬件开发

    如下:1、采用 单片机(PIC)设计架空线路单相故障指示器,其中大量模拟电路用于采集高压电缆电流电压,信号滤波放大处理,绘制4层PCB板、 软硬件 结合实现低功耗100uA。2、采样 STM32
    发表于12-16 16:51 13次下载
    专业<b class='flag-5'>软硬件</b>开发

    深度:单片机到底是如何软硬件结合的?

    分析 单片机 到底是如何 软硬件 结合的。并通过一个基本的程序,分析 单片机程序的编译,运行。
    发表于02-08 16:29 2次下载
    深度:<b class='flag-5'>单片机</b><b class='flag-5'>到底</b>是如何<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的?

    深度:单片机到底是如何软硬件结合的?

    本文我们将分析 单片机 到底是如何 软硬件 结合的,并通过一个基本的程序,分析 单片机程序的编译,运行。
    发表于02-08 16:53 7次下载
    深度:<b class='flag-5'>单片机</b><b class='flag-5'>到底</b>是如何<b class='flag-5'>软硬件</b><b class='flag-5'>结合</b>的?