STM32
直播中

李明

7年用户 1299经验值
私信 关注
[问答]

请问51单片机/STM32单片机/嵌入式Linux是如何点亮LED灯的?

请问51单片机/STM32单片机/嵌入式Linux是如何点亮LED灯的?

回帖(1)

王岩

2021-11-22 09:57:10

下面分别从51单片机、STM32单片机(寄存器、库函数、RTOS)、嵌入式Linux五个方面解释,这里我们假设都是低电平点亮。


51单片机点亮
#include "reg52.h"    //此文件中定义了单片机的一些特殊功能寄存器
/* reg52.h 里定义寄存器 sfr P2 = 0xA0*/
***it led=P2^0;           //将单片机的P2.0端口定义为led

void main()
{
        while(1)
        {
                led=0;        //P2.0端口设置为低电平
        }               
}
        上图中可以看到,“led = 0;”是最为顶层的代码,他将IO端口配置为低;中间层代码为“led = P2^0;”,他将变量和IO口对应的寄存器做了映射,方便顶层操作;可以看到最底层的代码在reg52.h里,“sfr P2 = 0xA0;”定义了P2IO口对应的首个端口的地址,方便去访问。


        综上所述,我们可以知道,从C语言的层面我们需要寄存器地址,最终配置的是寄存器的数值。同时,我们需要引入变量LED增加代码可读性。





STM32单片机-寄存器
/*******************************************************************************
*                 
*                                        普中科技
--------------------------------------------------------------------------------
* 实 验 名                 : 使用寄存器点亮一个LED
* 实验说明       : 操作寄存器控制D1指示灯闪烁
* 连接方式       :
* 注    意                 :        
*******************************************************************************/

#include "stm32f4xx.h"
/*
以下头文件stm32f4xx.h内容
#define PERIPH_BASE      ((unsigned int)0x40000000)
#define AHB1PERIPH_BASE  (PERIPH_BASE + 0x00020000)
#define GPIOF_BASE       (AHB1PERIPH_BASE + 0x1400)
#define GPIOF_MODER          *(unsigned int*)(GPIOF_BASE+0x00)
#define GPIOF_BSRR                  *(unsigned int*)(GPIOF_BASE+0x18)       
#define RCC_BASE                 (AHB1PERIPH_BASE + 0x3800)
#define RCC_AHB1ENR         *(unsigned int*)(RCC_BASE+0x30)
*/

typedef unsigned int u32;   //类型重定义 unsigned int -- u32

void SystemInit()
{
       
}


//* 函数功能                   : 延时函数,通过while循环占用CPU,达到延时功能
void delay(u32 i)
{
        while(i--);
}

//* 函数功能                   : 主函数
int main()
{
        RCC_AHB1ENR |= 1<<5;
        GPIOF_MODER = (1<<(2*9));
        while(1)
        {
                GPIOF_BSRR=(1<<(16+9));
                delay(0xFFFFF);
       
                GPIOF_BSRR=(1<<(9));
                delay(0xFFFFF);       
        }
}





#define PERIPH_BASE      ((unsigned int)0x40000000)  外设基地址(参考手册54页)





#define AHB1PERIPH_BASE  (PERIPH_BASE + 0x00020000)
#define GPIOF_BASE       (AHB1PERIPH_BASE + 0x1400)





#define GPIOF_MODER      *(unsigned int*)(GPIOF_BASE+0x00)





#define GPIOF_BSRR          *(unsigned int*)(GPIOF_BASE+0x18)




   
#define RCC_BASE         (AHB1PERIPH_BASE + 0x3800)





#define RCC_AHB1ENR     *(unsigned int*)(RCC_BASE+0x30)





我们配置使能时钟: RCC_AHB1ENR |= 1<<5;





我们设置GPIOF9的模式:GPIOF_MODER = (1<<(2*9));这里的1是01





我们设置GPIOF9的电平:GPIOF_BSRR=(1<<(16+9)); (复位点亮)    GPIOF_BSRR=(1<<(9));(置位熄灭)
        综上所述,相对于51单片机,STM32的单片机复杂度高在了复用功能的增多,导致的寄存器的增多,对于同样可以看到明显的代码分层现象。


STM32单片机-库函数



/*
标准库
void LED_Init()
{
        GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
       
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //使能端口F时钟
       
        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式
        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//管脚设置F9
        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M
        GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
        GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
        GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化结构体
        GPIO_SetBits(GPIOF,GPIO_Pin_9);
}
*/
#include "stm32f4xx.h"
#include "led.h"

int main()
{
        LED_Init();
        while(1)
        {
        /* 复位*/
                GPIO_ResetBits(GPIOF,GPIO_Pin_9);//复位F9 点亮D1
        }
}
可以看到,将地址define为字符,不需要在访问寄存器,利用给出的字符选项进行控制寄存器里的内容。


如果需要更简单的话,STM32CubeMax图形化界面就行。甚至MATLAB写的流程都可以转化为STM32代码(B站有教程)。


当然,毫无疑问的是编程的安全性(不直接接触地址)和简洁性(字符选项)得到了加强。





STM32单片机-RTOS
        这里我们以RT-Thread为例。在我看来RTT相较于裸机程序,主要是在几个方面不同:从while+中断----->线程+中断、将app驱动和设备驱动分离。也就是说我们要更加注重于不同任务的分离、通用型APP的使用。


        Drivers驱动文件夹对接硬件底层,将裸机代码里的参数对应到自己的C文件里;DeviceDrivers驱动文件夹对接APP,为开发者提供应用函数,当配置好参数就直接用DeviceDrivers提供的函数编程即可。


#include
#include

#define THREAD_PRIORITY         10
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        10
#define GPIOF9 89 //device对接底层

//static rt_thread_t tid1 = RT_NULL;


static void led_entry(void *parameter)
{
    rt_pin_mode(GPIOF9,PIN_MODE_OUTPUT);

    while (1)
    {
                rt_pin_write(GPIOF9, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(GPIOF9, PIN_LOW);
        rt_thread_mdelay(500);
                       
    }
}


void led_test(void)
{
                rt_thread_t tid1;
    /* 创建线程1,名称是thread1,入口是thread1_entry*/
    tid1 = rt_thread_create("thread1",
                            led_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
   
    /* 如果获得线程控制块,启动这个线程 */
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);
}
可以说除了89对应PF9外(芯片直接不同),其余参数不需要访问Drivers文件夹。分离做的非常好。


APP-->DeviceDrivers-->CPU-->Drivers-->HAL,函数的调用执行基本就是这样一个流程。





Linux的部分以后再做。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分