05 第4章作业 | 嵌入式

116 阅读8分钟
  1. 学习CH04示例程序,包括gpio.c和4个工程中的main.c.

Untitled 7.png


  1. 给出 gpio_set(LIGHT_ RED LIGHT_ OFF); 语句中,LIGHT_RED和LIGHT_ OFF的值是多少?贴出每一 步的查找截图。

    LIGHT_REDLIGHT_ OFF 在 user.h中定义

    Untitled 8.png

//指示灯端口及引脚定义
#define  LIGHT_RED    (PTB_NUM|7)  //红灯,(GEC_56)
//灯状态宏定义(灯亮、灯暗对应的物理电平由硬件接法决定)
#define  LIGHT_ON       0    //灯亮
#define  LIGHT_OFF      1    //灯暗

LIGHT_RED = (PTB_NUM|7)

PTB_NUM端口号地址偏移值 ,在gpio.h头文件中有定义

Untitled 9.png

PTB_NUM = 0b10000 0000 (1<<8)

LIGHT_RED的值 = PTB_NUM|7 = 0b10000 0111

LIGHT_OFF 对应值为 1


  • 执行 gpio_set(LIGHT_ RED LIGHT_ OFF); 的步骤

    调用在 03_MCU\MCU_drivers目录下的gpio.c中的gpio_set()函数

    Untitled 10.png

    LIGHT_ RED 对应参数 port_pin = (PTB_NUM|7)

    LIGHT_ OFF 对应参数 state = 1

    之后执行 gpio_get_port_pin(port_pin,&port,&pin);

    • 调用 gpio_get_port_pin()函数

      根据带入参数port_pin解析端口与引脚分别赋给port,pin

      Untitled 11.png

      则对应将LIGHT_ RED = 0b10000 0111 作为 port_pin 参数传入,并解析

      那么 *port = (port_pin>>8); 再右移8位 得到 端口号 port = 1

      *pin = port_pin; 得到 引脚号 pin = 7

    再返回到 gpio_set()函数中执行

    //根据port,给局部变量gpio_ptr赋值(GPIO基地址)
    if(7 == port) //GPIOH
            gpio_ptr = GPIO_ARR[port-2];
    else
            gpio_ptr = GPIO_ARR[port];
    
    //根据state,设置对应引脚状态
    if(1 == state)    //高电平(该引脚对应置位寄存器置1)
            gpio_ptr->BSRR = (uint32_t)(1u<<pin);
    else              //低电平(该引脚对应重置寄存器置1)
            gpio_ptr->BRR = (uint32_t)(1u<<pin);
    

    计算 GPIO基地址 gpio_ptr = GPIO_ARR[1];

    Untitled 12.png

    则 *gpio_ptr = (GPIO_TypeDef )GPIOB_BASE

    GPIOB_BASE = AHB2PERIPH_BASE + 0x0400UL Untitled 13.png

    AHB2PERIPH_BASE = PERIPH_BASE + 0x08000000UL Untitled 14.png

    PERIPH_BASE = 0x40000000UL Untitled 15.png

    对应gpio_ptr = 0x40000000UL + 0x08000000UL + 0x0400UL = 0x480000400UL

    GPIO B口基地址值0x480000400UL

    由于 state = 1,则 引脚状态值为 高电平

    if(1 == state)    //高电平(该引脚对应置位寄存器置1)
        gpio_ptr->BSRR = (uint32_t)(1u<<pin);
    

    128(即 1u << 7 的结果)作为参数写入 gpio_ptr->BSRR 寄存器,这将使与第 7 位相对应的 GPIO 引脚置为高电平(即设置为 1

    完成 gpio_set()函数 引脚状态 的设定


  1. 直接地址编程方式,实现红绿蓝三灯轮流闪烁

    在GPIO-Output-DirectAddress_STM32L431_20200928工程的main.c文件的基础上进行修改实现

    • 添加了红灯和绿灯的 GPIO 配置,设置它们为输出模式-01
    • 修改了主循环部分,在每次循环中,根据当前的灯状态标志 mFlag 来控制哪盏灯亮,并在下次循环前关闭所有灯
    • 通过switch语句控制灯的状态标志切换,以及控制哪盏灯亮
    • 添加了延时函数,使得每次灯亮1s后才会切换,方便观察结果
    #define GLOBLE_VAR
    #include "includes.h"      //包含总头文件
    
    //----------------------------------------------------------------------
    //声明使用到的内部函数
    //main.c使用的内部函数声明处
    void Delay_ms(uint16_t u16ms);
    
    //----------------------------------------------------------------------
    //主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程见书稿)
    int main(void)
    {
        //(1)======启动部分(开头)==========================================
        //(1.1)声明main函数使用的局部变量
        uint32_t mMainLoopCount;  //主循环使用的记录主循环次数变量
        uint8_t  mFlag;            //主循环使用的临时变量
    
        //(1.2)【不变】关总中断
        DISABLE_INTERRUPTS;
    
        //(1.3)给主函数使用的局部变量赋初值
        mMainLoopCount = 0;     //主循环使用的记录主循环次数变量
        mFlag='R';              //主循环使用的临时变量:当前亮的灯('R':红灯,'G':绿灯,'B':蓝灯)
    
        //(1.4)给全局变量赋初值
    
        //(1.5)用户外设模块初始化 user.
        // B口7脚(红灯,低电平点亮)
        // B口8脚(绿灯,低电平点亮)
        // B口9脚(蓝灯,低电平点亮)
        //(1.5.1)声明变量
        volatile uint32_t* RCC_AHB2;    //GPIO的B口时钟使能寄存器地址
        volatile uint32_t* gpio_ptr;    //GPIO的B口基地址
        volatile uint32_t* gpio_mode;   //引脚模式寄存器地址=口基地址
                    volatile uint32_t* gpio_bsrr;   //置位/复位寄存器地址
                    volatile uint32_t* gpio_brr;    //GPIO位复位寄存器
    
            //(1.5.2)变量赋值
        RCC_AHB2=(uint32_t*)0x4002104C;   //GPIO的B口时钟使能寄存器地址
                    gpio_ptr=(uint32_t*)0x48000400;   //GPIO的B口基地址
                    gpio_mode=gpio_ptr;    //引脚模式寄存器地址=口基地址
        gpio_bsrr=gpio_ptr+6;  //置位/复位寄存器地址
        gpio_brr=gpio_ptr+10;  //GPIO位复位寄存器
    
        //(1.5.3)GPIO初始化
        //(1.5.3.1)使能相应GPIOB的时钟
        *RCC_AHB2|=(1<<1);       //GPIOB的B口时钟使能
    
        // (1.5.3.1)设置 B7、B8、B9 引脚为输出引脚-01
        //  定义B口7脚为输出引脚(令D15、D14=01)
        *gpio_mode &= ~(3<<14); //15,14位 置0 
        *gpio_mode |=(1<<14);   //14位置1 
    
        //  定义B口8脚为输出引脚(令D17、D16=01)
        *gpio_mode &= ~(3<<16); //16,17位 置0 
        *gpio_mode |=(1<<16);   //16位置1
    
        //  定义B口9脚为输出引脚(令D19、D18=01)
        *gpio_mode &= ~(3<<18);  
        *gpio_mode |=(1<<18);   
    
        //(1.6)使能模块中断
        //(1.7)【不变】开总中断
        ENABLE_INTERRUPTS; 
    
        //(2)======主循环部分(开头)=========================================
        for(;;)     //for(;;)(开头)
        {
            //(2.1)主循环次数+1,并判断是否小于特定常数
            mMainLoopCount++;                         //+1
            if (mMainLoopCount<=6556677)  continue;   //如果小于特定常数,继续循环
            //(2.2)主循环次数超过特定常数,灯状态进行切换(这样灯会闪烁)
            mMainLoopCount=0;      //清主循环次数
    
            // 根据当前灯的状态标志切换灯状态
            switch (mFlag) {
                case 'R':  // 红灯
                    *gpio_brr |= (1 << 7);  // 点亮红灯
                    printf("红灯:亮\r\n");
                    mFlag = 'G';  // 切换到绿灯
                    break;
                case 'G':  // 绿灯
                    *gpio_brr |= (1 << 8);  // 点亮绿灯
                    printf("绿灯:亮\r\n");
                    mFlag = 'B';  // 切换到蓝灯
                    break;
                case 'B':  // 蓝灯
                    *gpio_brr |= (1 << 9);  // 点亮蓝灯
                    printf("蓝灯:亮\r\n");
                    mFlag = 'R';  // 切换到红灯
                    break;
            }
            // 在下一次循环开始前,关闭所有灯,从而保证下一次只有一种颜色的灯亮
            *gpio_bsrr |= ((1 << 7) | (1 << 8) | (1 << 9));  // 关闭所有灯
    
            //延迟1s
            Delay_ms(1000);
    
        }     
        //(2)======主循环部分(结尾)========================================    
    }
    //函数名称:Delay_ms
    //函数返回:无
    //参数说明:近似毫秒
    //功能概要:延时 - 毫秒级
    //======================================================================
    void Delay_ms(uint16_t u16ms)
    {
        for(volatile uint32_t i = 0; i < 8000*u16ms; i++) __asm("NOP");
    }
    

    编译后,进行串口更新的结果:

    Untitled 16.png

  1. 调用构件方式,实现红绿蓝的八种组合轮流闪烁

    在GPIO-Output-Component_STM32L431_20200928工程的main.c文件基础上进行修改,参照CH01中的示例进行实现

    Untitled 17.png

    设置红绿蓝的八种组合一个循环40s,每5s更换

    暗-红-绿-黄-蓝-紫-青-白顺序亮灯

    #define GLOBLE_VAR
    #include "includes.h"      //包含总头文件
    
    //----------------------------------------------------------------------
    //声明使用到的内部函数
    //main.c使用的内部函数声明处
    void Delay_ms(uint16_t u16ms);
    //----------------------------------------------------------------------
    //主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程,参见书稿)
    int main(void)
    {
        //(1)======启动部分(开头)==========================================
        //(1.1)声明main函数使用的局部变量
        uint32_t mCount;			//延时的次数
    
        //(1.2)【不变】关总中断
        DISABLE_INTERRUPTS;
    
        //(1.3)给主函数使用的局部变量赋初值
        mCount=0;//记次数
    
        //(1.4)给全局变量赋初值
        //(1.5)用户外设模块初始化
        gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);		//初始化红灯
        gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);		//初始化绿灯
        gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);		//初始化蓝灯
    
        uart_init(UART_User,115200);     //初始化串口模块
    
        //(1.6)使能模块中断
        uart_enable_re_int(UART_User);
    
        //(1.7)【不变】开总中断
        ENABLE_INTERRUPTS;
    
    
        //(1)======启动部分(结尾)==========================================
    
        //(2)======主循环部分(开头)========================================
        for(;;)   //for(;;)(开头)
        {
            //延时1秒
            Delay_ms(1000);
            if(mCount==0)
            {
                    printf("指示灯颜色为【暗色】\n");
            }
            mCount++;//记录秒数
            //八种组合一次循环时间为40s,当秒数40秒时,重新开始计数
            //避免一直累加
            if (mCount >= 40)
            {
                mCount=0;
            }
            //实现红绿蓝的八种组合轮流闪烁,每5s一切换
            if(mCount%5==0)
            {
                gpio_reverse(LIGHT_RED);
                printf(" LIGHT_RED:reverse--\n");   //串口输出灯的状态
                if(mCount/5==1)
                {
                    printf("指示灯颜色为【红色】\n");
                }
                else if(mCount/5==3)
                {
                    printf("指示灯颜色为【黄色】\n");
                }
                else if(mCount/5==5)
                {
                    printf("指示灯颜色为【紫色】\n");
                }
                else if(mCount/5==7)
                {
                    printf("指示灯颜色为【白色】\n");
                    printf("------------------------------------------\n");
                }
            }
            if (mCount%10==0)                    //判断灯的状态标志
            {
                gpio_reverse(LIGHT_GREEN);
                printf(" LIGHT_GREEN:reverse--\n");   //串口输出灯的状态
                if(mCount/10==1)
                {
                    printf("指示灯颜色为【绿色】\n");
                }
                else if(mCount/10==3)
                {
                    printf("指示灯颜色为【青色】\n");
                }
            }
            if (mCount%20==0)                    //判断灯的状态标志
            {
                gpio_reverse(LIGHT_BLUE);				
                printf(" LIGHT_BLUE:reverse--\n");   //串口输出灯的状态
                if(mCount/20==1)
                {
                    printf("指示灯颜色为【蓝色】\n");
                }
    
            }
    
        } 		//for(;;)结尾
        //(2)======主循环部分(结尾)========================================
    }   //main函数(结尾)
    
    //======以下为主函数调用的子函数===========================================
    //======================================================================
    //函数名称:Delay_ms
    //函数返回:无
    //参数说明:近似毫秒
    //功能概要:延时 - 毫秒级
    //======================================================================
    void Delay_ms(uint16_t u16ms)
    {
        for(volatile uint32_t i = 0; i < 8000*u16ms; i++) __asm("NOP");
    }
    
    

    编译后,进行串口更新的结果:

    Untitled 18.png Untitled 19.png Untitled 20.png