搜索
bottom↓
回复: 0

《STM32MP1 M4裸机HAL库开发指南》第十二章 C语言LED灯实验

[复制链接]

出0入234汤圆

发表于 2022-10-28 11:34:53 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-10-28 11:34 编辑

1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png
lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png


第十二章 C语言LED灯实验

        
        为了加深理解汇编语言以及汇编初始化过程,第十一章我们使用汇编来控制LED0。本章节我们来学习使用C语言来控制LED0,实际的开发中我们接触最多的就是C语言了,在整个裸机开发中,基本上也都是使用C语言来完成。
        本章将分为如下几个小节:
        12.1、C语言控制LED灯简介;
        12.2、硬件设计;
        12.3、软件设计;
        12.4、编译和测试;

12.1 C语言控制LED灯简介

        第十一章的实验是用汇编语言来控制LED0闪烁的,学习该章节的目的也就是为了加深理解汇编的初始化过程,而实际的开发中我们很少使用汇编来开发,毕竟汇编比较难,可移植性也不高。不管怎样,学习和理解汇编还是很有必要的,汇编部分主要是在芯片上电后的初始化工作,当这些工作完成后才会进入C的世界,所以,不管是用汇编语言来开发,还是使用C语言来开发,前提都是离不开汇编的启动文件startup_stm32mp15xx.s。
        本章节我们来学习使用C语言完成LED灯的控制,实际上ST官方提供的HAL库也是C语言,只是ST把具有通用性的函数封装成为接口,我们通过调用接口可以实现对应的功能,这大大提高了开发的效率以及移植性,不过这里我们先不用HAL库来开发,我们先自行编写C语言代码来实现。
12.2 硬件设计
        硬件原理图和第十一章节一样。本章节控制的是开发板的LED0和LED1。
第十二章 C语言LED灯实验628.png
图12.2.1 LED与STM32MP157连接原理图

        可以看出,LED0 接到了PI0引脚上,当PI0输出低电平(0)的时候发光二极管 LED0 就会导通点亮,当PI0输出高电平(1)的时候发光二极管 LED0 不会导通,因此 LED0 也就不会点亮。LED1接在了PF3引脚上,同理,LED1的亮灭取决于PF3的输出电平,输出 0 就亮,输出 1 就灭。
12.3 软件设计
        本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\3、M4裸机驱动例程\ MP157-M4 HAL库V1.2\实验1 C语言LED灯实验。
12.3.1 创建工程
        实验的第一步都是创建工程,创建工程步骤可参考前面第六章部分。这里我们新建三个文件:启动文件startup_stm32mp15xx.s、main.c文件、main.h文件,如下图:
第十二章 C语言LED灯实验1016.png
图12.3.1.1新建工程

12.3.2 代码编写
1. 修改startup_stm32mp15xx.s
        我们直接拷贝STM32Cube固件包里的启动文件的代码到本工程的startup_stm32mp15xx.s文件中,文件在固件包的路径为STM32Cube_FW_MP1_V1.2.0\Drivers\CMSIS\Device\ST\STM32MP1xx\Source\Templates\arm\startup_stm32mp15xx.s。拷贝完成后,我们修改一下地方:
        IMPORT表示标号来自外部文件,本工程中没有SystemInit函数,所以我们注释掉第243行、246行、247行;本工程中只有main函数,所以224行和248行的标号__main改为main,最后修改的部分如下图所示:
第十二章 C语言LED灯实验1371.png
图12.3.2.1修改启动文件

2. 添加main.h代码
        main.h文件中主要是本工程中的GPIO相关的寄存器地址定义,这个部分其实也就和我们以前编写51单片机的代码类似,main.h的代码如下:
#ifndef __MAIN_H
#define __MAIN_H

/*
* 各个外设基地址
*/
#define PERIPH_BASE             (0x40000000)
#define MCU_AHB4_PERIPH_BASE    (PERIPH_BASE + 0x10000000)
#define RCC_BASE                (MCU_AHB4_PERIPH_BASE + 0x0000)
#define GPIOI_BASE              (MCU_AHB4_PERIPH_BASE + 0xA000)
#define GPIOF_BASE              (MCU_AHB4_PERIPH_BASE + 0x7000)
/*
* 寄存器地址
*/
#define RCC_MC_AHB4ENSETR  *((volatile unsigned int *)(RCC_BASE + 0XAA8))
#define GPIOI_MODER        *((volatile unsigned int *)(GPIOI_BASE + 0x0000))   
#define GPIOI_OTYPER       *((volatile unsigned int *)(GPIOI_BASE + 0x0004))
#define GPIOI_OSPEEDR      *((volatile unsigned int *)(GPIOI_BASE + 0x0008))   
#define GPIOI_PUPDR        *((volatile unsigned int *)(GPIOI_BASE + 0x000C))
#define GPIOI_BSRR         *((volatile unsigned int *)(GPIOI_BASE + 0x0018))

#define GPIOF_MODER        *((volatile unsigned int *)(GPIOF_BASE + 0x0000))   
#define GPIOF_OTYPER       *((volatile unsigned int *)(GPIOF_BASE + 0x0004))   
#define GPIOF_OSPEEDR      *((volatile unsigned int *)(GPIOF_BASE + 0x0008))   
#define GPIOF_PUPDR        *((volatile unsigned int *)(GPIOF_BASE + 0x000C))   
#define GPIOF_BSRR         *((volatile unsigned int *)(GPIOF_BASE + 0x0018))

#define OFF     0      
#define ON      1      

#endif
        main.h中,最后定义以下地址:
7E488B9F-9AA2-47fe-8359-A7F7839BD1BF.png
表12.3.2.1本工程涉及的寄存器地址

3. 添加main.c文件代码
        main.c文件代码如下:
/**
***********************************************************************
* @file        main.c
* @author      正点原子Linux团队(ALIENTEK)
* @version     V1.0
* @date        2020-05-03
* @brief       C语言点灯 实验
* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
***********************************************************************
* @attention
*
* 实验平台:正点原子 STM32MP1开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
**********************************************************************
*/
#include "main.h"

/**
* @brief     使能相关时钟
* @param     无
* @retval    无
*/
void clk_enable(void)
{
    RCC_MC_AHB4ENSETR |= ((unsigned int)1 << 8);    /* 使能GPIOI时钟 */
    RCC_MC_AHB4ENSETR |= ((unsigned int)1 << 5);    /* 使能GPIOF时钟 */
}

/**
* @brief     初始化GPIO
* @param     无
* @retval    无
*/
void gpio_init(void)
{
    /* GPIOI_0设置为输出 */
    GPIOI_MODER &= ~((unsigned int)3 << (2 * 0));
    GPIOI_MODER |= ((unsigned int)1 << (2 * 0));
   
    /* GPIOI_0设置为推挽模式 */
    GPIOI_OTYPER &= ~((unsigned int)1 << 0);
   
    /* GPIOI_0设置为高速模式 */
    GPIOI_OSPEEDR &= ~((unsigned int)3 << (2 * 0));
    GPIOI_OSPEEDR |= ((unsigned int)2 << (2 * 0));
   
    /* GPIOI_0设置为上拉 */
    GPIOI_PUPDR &= ~((unsigned int)3 << (2 * 0));
    GPIOI_PUPDR |= ((unsigned int)1 << (2 * 0));
   
    /* GPIOF_3设置为输出 */
    GPIOF_MODER &= ~((unsigned int)3 << (2 * 3));
    GPIOF_MODER |= ((unsigned int)1 << (2 * 3));
   
    /* GPIOF_3设置为推挽模式 */
    GPIOF_OTYPER &= ~((unsigned int)1 << 3);
   
    /* GPIOF_3设置为高速模式 */
    GPIOF_OSPEEDR &= ~((unsigned int)3 << (2 * 3));
    GPIOF_OSPEEDR |= ((unsigned int)2 << (2 * 3));
   
    /* GPIOF_3设置为上拉 */
    GPIOF_PUPDR &= ~((unsigned int)3 << (2 * 3));
    GPIOF_PUPDR |= ((unsigned int)1 << (2 * 3));
}

/**
* @brief     LED0开关函数
* @param     无
* @retval    无
*/
void led0_switch(unsigned char state)
{
    if(state == OFF)
    {
        GPIOI_BSRR |= ((unsigned int)1 << 0);   /* GPIOI_0输出高电平 */
    } else if(state == ON)
    {
        GPIOI_BSRR |= ((unsigned int)1 << 16);  /* GPIOI_0输出低电平 */
    }
}

/**
* @brief     LED1开关函数
* @param     无
* @retval    无
*/
void led1_switch(unsigned char state)
{
    if(state == OFF)
    {
        GPIOF_BSRR |= ((unsigned int)1 << 3);   /* GPIOF_3输出高电平 */
    } else if(state == ON)
    {
        GPIOF_BSRR |= ((unsigned int)1 << 19);  /* GPIOF_3输出低电平 */
    }
}

/**
* @brief       短时间延时函数
* @param - n   要延时循环次数(空操作循环次数,模式延时)
* @retval      无
*/
void delay_short(volatile unsigned int n)
{
    while(n--){}
}

/**
* @brief       长延时函数
* @param - n   要延时的时间循环数
* @retval      无
*/
void delay(volatile unsigned int n)
{
    while(n--)
    {
        delay_short(0x7fff);
    }
}

/**
* @brief       main函数
* @param       无
* @retval      无
*/
int main(void)
{
    clk_enable();   /* 使能时钟 */
    gpio_init();    /* 初始化GPIO */
   
    while(1)
    {
        led0_switch(ON);        /* LED0开 */
        led1_switch(OFF); /* LED1关 */
        delay(100);                /* 延时一定时间 */
        led0_switch(OFF);        /* LED0关 */
        led1_switch(ON);        /* LED1开 */
        delay(100);                /* 延时一定时间 */
    }   
}
        main.c文件的代码比较简单:
        clk_enable函数主要完成PI0和PF3的时钟开启,这里注意的是,我们没有去配置任何时钟,所以复位以后,系统默认使用内部高速时钟HSI(64MHz)来工作,此时AHB4总线的时钟为64MHz,而GIOI和GPIOF都挂在AHB4总线上,所以GPIOI和GPIOF的时钟频率也为64MHz;
        gpio_init函数PI0和PF3的初始化:设置引脚为推挽输出、上拉、高速模式;
        led0_switch函数设置PI0引脚为高或低电平;
        led1_switch函数设置PF3引脚为高或低电平;
        delay函数调用delay_short函数完成时钟延时功能,这里通过让CPU做空循环达到延时的效果;
        main函数通过调用以上函数,由while循环实现LED0和LED1交替闪烁。
12.3.3 编译和测试
        编译工程无报错,进入仿真模式运行,最后看到LED0和LED1交替闪所,实验完成。

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-27 12:57

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表