理解IO驱动,51单片机、STM32单片机、嵌入式Linux分别点亮LED灯

无论是学开发什么板子,接触的第一个程序一般都是点亮LED灯。

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

51单片机点亮

#include "reg52.h"    //此文件中定义了单片机的一些特殊功能寄存器
/* reg52.h 里定义寄存器 sfr P2 = 0xA0*/
sbit 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页)

理解IO驱动,51单片机、STM32单片机、嵌入式Linux分别点亮LED灯_第1张图片
#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;

理解IO驱动,51单片机、STM32单片机、嵌入式Linux分别点亮LED灯_第2张图片
我们设置GPIOF9的模式:GPIOF_MODER = (1<<(2*9));这里的1是01

理解IO驱动,51单片机、STM32单片机、嵌入式Linux分别点亮LED灯_第3张图片
我们设置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的部分以后再做。

 

 

 

你可能感兴趣的:(RT-T,---------,嵌入式Linux,---------)