zoukankan      html  css  js  c++  java
  • 【STM32H7教程】第60章 STM32H7的DAC应用之定时器触发实现DMA方式双通道波形

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    第60章       STM32H7的DAC应用之定时器触发实现DMA方式双通道波形

    本章节为大家讲解DAC采用定时器触发方式实现DMA双通道波形输出,实际输出效果也比较好,项目使用价值也比较大。

    60.1 初学者重要提示

    60.2 H7和F4的DAC输出效果对比

    60.3 DAC驱动设计

    60.4 DAC驱动移植和使用

    60.5 实验例程设计框架

    60.6 实验例程说明(MDK)

    60.7 实验例程说明(IAR)

    60.8 总结

    60.1 初学者重要提示

    1.   学习本章节前,务必优先学习第59章,需要对DAC的基础知识有个认识。
    2.   开发板右上角有个跳线帽,可以让ADC的稳压基准接3.3V或者2.5V,本章例子是接到3.3V。
    3.   注意STM32H7只有一个DAC,但有两个独立的通道,跟F4的略不同,F4是两个DAC。
    4.   如果仅使用STM32H7的一个通道,即PA4或者PA5引脚,另一个引脚没有做任何配置,这个引脚上会有波形效应。
    5.   STM32H7的DAC支持出厂校准和用户校准模式。特别注意一点,校准是建立在用户使能了输出缓冲的情况下才有效。
    6.   STM32H7的DAC支持正常模式和采样保持模式,其中采样保持模式用于低功耗状态使用。
    7.   DAC的输出除了可以连接PA4或者PA5引脚,也可以连接到片上外设,比如运放,比较器。

    60.2 DAC稳压基准硬件设计

    详见第46章的第2小节,有详细说明,ADC和DAC使用的基准源是一样的。

    60.3 H7和F4的DAC输出效果对比

    STM32H7的DAC输出100KHz方波的效果比F429好不少,满幅输出。

    STM32H743输出100KHz的效果如下:

     

    STM32F429输出100KHz的效果如下:

     

    60.4 DAC驱动设计

    60.4.1 DAC驱动设计框架

    为了方便大家理解DAC驱动的实现,先看下面DAC的驱动设计框架:DAC做定时器触发 + DMA数据传输的实现思路框图如下:

     

    下面为大家讲解具体的驱动实现。

    60.4.2 第1步:DAC配置

    DAC的配置比较简单,仅需如下代码即可:

    DAC_HandleTypeDef   DAC_Handle;
    DacHandle.Instance = DAC1;
    if (HAL_DAC_Init(&DacHandle) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }

    60.4.3 第2步:DAC通道配置

    下面是DAC通道1的配置,如果配置通道2的话,也是同样的方式:

    1.    static DAC_HandleTypeDef      DacHandle;
    2.    static DAC_ChannelConfTypeDef sConfig;
    3.    
    4.    sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
    5.    sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
    6.    sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
    7.    sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
    8.    sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
    9.    
    10.    if (HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DAC_CHANNEL_1) != HAL_OK)
    11.    {
    12.        Error_Handler(__FILE__, __LINE__);
    13.    }

    下面将程序设计中几个关键地方做个阐释:

    •   第4行,关闭采样保持模式,这个模式主要用于低功耗。
    •   第5行,采用TIM6作为触发源。
    •   第6行,使能DAC输出缓冲,增加驱动能力。
    •   第7行,关闭DAC的输出连接片上外设,这样DAC的输出是连接的PA4或者PA5引脚。
    •   第8行,采用出厂校准。
    •   第10行,配置DAC的通道1。

    60.4.4 第3步:DMA配置

    DAC通道1的DMA配置如下,如果使用通道2,配置是类似的,代码如下:

    1.    static DMA_HandleTypeDef      hdma_dac1;
    2.    
    3.    hdma_dac1.Instance = DMA1_Stream0;              /* 使用的DAM1 Stream0 */
    4.    hdma_dac1.Init.Request  = DMA_REQUEST_DAC1;     /* DAC触发DMA传输 */
    5.    hdma_dac1.Init.Direction = DMA_MEMORY_TO_PERIPH;/* 存储器到外设 */
    6.    hdma_dac1.Init.PeriphInc = DMA_PINC_DISABLE;    /* 外设地址禁止自增 */
    7.    hdma_dac1.Init.MemInc = DMA_MINC_ENABLE;        /* 存储器地址自增 */
    8.    hdma_dac1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* 外输操作数据宽度,半字 */
    9.    hdma_dac1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;    /* 存储器操作数据宽度,半字 */
    10.    hdma_dac1.Init.Mode = DMA_CIRCULAR;                           /* 循环模式 */
    11.    hdma_dac1.Init.Priority = DMA_PRIORITY_HIGH;                  /* 优先级高 */
    12.    
    13.    HAL_DMA_Init(&hdma_dac1);
    14.    
    15.    /* 关联DMA句柄到DAC句柄下 */
    16.    __HAL_LINKDMA(&DacHandle, DMA_Handle1, hdma_dac1);
    17.    
    18.    /* 启动DAC DMA */
    19.    if (HAL_DAC_Start_DMA(&DacHandle, DAC_CHANNEL_1, (uint32_t *)g_usWaveBuff, 64, DAC_ALIGN_12B_R) != HAL_OK)
    20.    {
    21.        Error_Handler(__FILE__, __LINE__);
    22.    }

    下面将程序设计中几个关键地方做个阐释:

    •   第3-11行,配置DAC触发DMA传输,这里是采用循环模式,让DMA做连续的数据传输。
    •   第16行,关联DMA的句柄到DAC,方便DAC后期调用。此时就要特别注意,变量hdma_dac1如果是局部变量的话,一定要设置为静态static,否则退出函数后,此变量会被释放掉。
    •   第19-22行,启动DAC的DMA方式传输。

    60.4.5 第4步:定时器触发

    为了方便控制DAC输出波形的频率,我们采用定时器触发:

    /*
    *********************************************************************************************************
    *    函 数 名: TIM6_Config
    *    功能说明: 配置定时器6,用于触发DAC。
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void TIM6_Config(void)
    {
        /*-----------------------------------------------------------------------
            bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下: 
    
            System Clock source       = PLL (HSE)
            SYSCLK(Hz)                = 400000000 (CPU Clock)
            HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
            AHB Prescaler             = 2
            D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
            D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
            D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
            D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)
    
            因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;
            因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
            APB4上面的TIMxCLK没有分频,所以就是100MHz;
    
            APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
            APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17
    
            APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
        
        TIM6 更新周期是 = TIM6CLK / (Period + 1)/(Prescaler + 1)
        根据如下的配置,更新周期是:
        TIM6CLK /(Period + 1)/(Prescaler + 1)
        = 200MHz /(30+1)/(0+1)
        ≈ 6.45MHz
        ----------------------------------------------------------------------- */
        TIM_MasterConfigTypeDef sMasterConfig;
        
        /* TIM6 时钟使能 */
        __HAL_RCC_TIM6_CLK_ENABLE();
    
        /* 配置定时器外设 */
        htim.Instance = TIM6;
    
        htim.Init.Period            = 30;
        htim.Init.Prescaler         = 0;
        htim.Init.ClockDivision     = 0;
        htim.Init.CounterMode       = TIM_COUNTERMODE_UP;
        htim.Init.RepetitionCounter = 0;
        HAL_TIM_Base_Init(&htim);
    
        /* TIM6 TRGO 选择 */
        sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
        sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    
        HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig);
    
        /* 使能定时器 */
        HAL_TIM_Base_Start(&htim);
    }

    定时器部分在前面章节有详细介绍,这里主要是定时器触发频率:

    TIM6 触发频率 = TIM6CLK / (Period + 1)/(Prescaler + 1)

    根据如下的配置,触发频率为:

    TIM6CLK /(Period + 1)/(Prescaler + 1)

    = 200MHz /(30+1)/(0+1)

    ≈ 6.45MHz

    TIM6每次触发都会启动一次数据传输,通过DMA方式将存储器中的数据传输到DAC寄存器中。如此以来,比如我们设置64个数据为一个波形周期,那么输出波形的频率就是6.45MHz / 64 ≈ 100KHz。

    60.4.6 第5步:波形数据生成

    测试DAC的输出波形效果,最好的方式就是输出高频率的方波,然后查看方波的棱角是否直,如果直的话,说明高频成分越丰富,方波效果越好。

    所以程序这里是直接设置64个点为一个周期的方波。

    1.    #if defined ( __ICCARM__ )
    2.    #pragma location = ".RAM_D3"  
    3.    ALIGN_32BYTES(uint16_t g_usWaveBuff[64]);
    4.    
    5.    #elif defined ( __CC_ARM )
    6.    ALIGN_32BYTES(__attribute__((section (".RAM_D3"))) uint16_t g_usWaveBuff[64]);
    7.    #endif
    8.    
    9.    /*
    10.    ******************************************************************************************************
    11.    *    函 数 名: bsp_InitDAC
    12.    *    功能说明: DAC初始化
    13.    *    形    参: 无
    14.    *    返 回 值: 无
    15.    ******************************************************************************************************
    16.    */
    17.    void bsp_InitDAC(void)
    18.    {   
    19.        uint8_t i;
    20.    
    21.        /* 一个周期的方波 */
    22.        for(i =0; i < 32; i++)
    23.        {
    24.            g_usWaveBuff[i] = 0;
    25.        }
    26.        
    27.        for(i =0; i < 32; i++)
    28.        {
    29.            g_usWaveBuff[i+32] = 4095;
    30.        }
    31.        
    32.        DAC_WaveConfig();
    33.        TIM6_Config(); 
    34.    }

    下面将程序设计中几个关键地方做个阐释:

    •   第2-3行,用于IAR编译器,这里是在D3域的SRAM4中定义一个数组。这种定义方式在第26章有详细说明。另外注意,由于工程里面是将TCM作为主RAM空间,而这个空间是不支持DMA1和DMA2进行操作的,所以我们这里是在SRAM4中定义一个数组。

    这里还通过ALIGN_32BYTES做了一个32字节对齐,主要是方便Cache相关的API调用。原始定义如下:

    #if defined   (__GNUC__)      /* GNU Compiler */
      #define ALIGN_32BYTES(buf)  buf __attribute__ ((aligned (32)))                                    
    #elif defined (__ICCARM__)    /* IAR Compiler */
      #define ALIGN_32BYTES(buf) _Pragma("data_alignment=32") buf  
    #elif defined   (__CC_ARM)    /* ARM Compiler */
      #define ALIGN_32BYTES(buf) __align(32) buf  
    #endif
    •   第6行,同上,用于MDK编译器。
    •   第22-30行,将64点数据一半设置为0,一半设置为12bit DAC的最大值4095。这里特别注意一点,程序里面是配置SRAM4的MPU属性为Write through,即数据就直接写入到SRAM4里面,无需再调用Cache的Clean函数。
    /* 配置SRAM4的MPU属性为Write through, read allocate,no write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x38000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    60.5 DAC驱动移植和使用

    DAC驱动的移植比较方便:

    •   第1步:复制bsp_dac.c和bsp_dac.h到自己的工程目录,并添加到工程里面。
    •   第2步:这几个驱动文件主要用到HAL库的GPIO、TIM,DMA和DAC驱动文件,简单省事些可以添加所有HAL库.C源文件进来。
    •   第3步,应用方法看本章节配套例子即可,另外就是根据自己的需要做配置修改。

    60.6 实验例程设计框架

    通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

     

      第1阶段,上电启动阶段:

    • 这部分在第14章进行了详细说明。

      第2阶段,进入main函数:

    •   第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED ,LCD,和SDRAM。
    •   第2步,PA4和PA5引脚同步输出100KHz方波。

    60.7 实验例程说明(MDK)

    配套例子:

    V7-037_DAC定时器触发+DMA方式双通道同步输出

    实验目的:

    1. 学习DAC定时器触发 + DMA方式双通道同步输出

    实验内容:

    1. 创建1个500ms的自动重载软定时器,每500ms翻转一次LED2。
    2. PA4和PA5引脚输出100KHz的方波。

    PA4和PA5引脚位置(稳压基准要短接3.3V):

     

    双通道100KHz方波效果:

     

    上电后串口打印的信息:

    波特率 115200,数据位 8,奇偶校验位无,停止位 1

     

    程序设计:

      系统栈大小分配:

     

      RAM空间用的DTCM:

     

      硬件外设初始化

    硬件外设的初始化是在 bsp.c 文件实现:

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_Init
    *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    *    形    参:无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_Init(void)
    {
        /* 配置MPU */
        MPU_Config();
        
        /* 使能L1 Cache */
        CPU_CACHE_Enable();
    
        /* 
           STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
           - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
           - 设置NVIV优先级分组为4。
         */
        HAL_Init();
    
        /* 
           配置系统时钟到400MHz
           - 切换使用HSE。
           - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
        */
        SystemClock_Config();
    
        /* 
           Event Recorder:
           - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
           - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
        */    
    #if Enable_EventRecorder == 1  
        /* 初始化EventRecorder并开启 */
        EventRecorderInitialize(EventRecordAll, 1U);
        EventRecorderStart();
    #endif
        
        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
        bsp_InitTimer();      /* 初始化滴答定时器 */
        bsp_InitUart();    /* 初始化串口 */
        bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
        bsp_InitLed();        /* 初始化LED */    
    
        bsp_InitDAC();      /* 初始化DAC */
    
    }

      MPU配置和Cache配置:

    数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和D3域的SRAM4。DAC的数据缓存开在了SRAM4。

    /*
    *********************************************************************************************************
    *    函 数 名: MPU_Config
    *    功能说明: 配置MPU
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void MPU_Config( void )
    {
        MPU_Region_InitTypeDef MPU_InitStruct;
    
        /* 禁止 MPU */
        HAL_MPU_Disable();
    
        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x24000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        
        /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x60000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        /* 配置SRAM4的MPU属性为Write through, read allocate,no write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x38000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: CPU_CACHE_Enable
    *    功能说明: 使能L1 Cache
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void CPU_CACHE_Enable(void)
    {
        /* 使能 I-Cache */
        SCB_EnableICache();
    
        /* 使能 D-Cache */
        SCB_EnableDCache();
    }

      主功能

    主程序实现如下操作:

    •   启动1个500ms的自动重装定时器,让LED2每500ms翻转一次。
    •   PA4和PA5引脚输出100KHz的方波。
    /*
    *********************************************************************************************************
    *    函 数 名: main
    *    功能说明: c程序入口
    *    形    参: 无
    *    返 回 值: 错误代码(无需处理)
    *********************************************************************************************************
    */
    int main(void)
    {
        uint8_t ucKeyCode;        /* 按键代码 */
    
    #if defined ( __CC_ARM )    
        TempValues1 = 0; /* 避免MDK警告 */  
        TempValues2 = 0;    
    #endif    
        
        bsp_Init();        /* 硬件初始化 */
        
        PrintfLogo();    /* 打印例程名称和版本等信息 */
        PrintfHelp();    /* 打印操作提示 */
    
        bsp_StartAutoTimer(0, 500);    /* 启动1个500ms的自动重装的定时器 */
        
        /* 进入主程序循环体 */
        while (1)
        {
            bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    
            /* 判断定时器超时时间 */
            if (bsp_CheckTimer(0))    
            {
                /* 每隔100ms 进来一次 */  
                bsp_LedToggle(2);
            }
    
            /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
            ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
            if (ucKeyCode != KEY_NONE)
            {
                switch (ucKeyCode)
                {
                    case KEY_DOWN_K1:        /* K1键按下 */
                        printf("K1按键按下
    ");
                        break;
    
                    default:
                        /* 其它的键值不处理 */
                        break;
                }
            }
        }
    }

    60.8 实验例程说明(IAR)

    配套例子:

    V7-037_DAC定时器触发+DMA方式双通道同步输出

    实验目的:

    1. 学习DAC定时器触发 + DMA方式双通道同步输出

    实验内容:

    1. 创建1个500ms的自动重载软定时器,每500ms翻转一次LED2。
    2. PA4和PA5引脚输出100KHz的方波。

    PA4和PA5引脚位置(稳压基准要短接3.3V):

     

    双通道100KHz方波效果:

     

    上电后串口打印的信息:

    波特率 115200,数据位 8,奇偶校验位无,停止位 1

     

    程序设计:

      系统栈大小分配:

     

      RAM空间用的DTCM:

     

      硬件外设初始化

    硬件外设的初始化是在 bsp.c 文件实现:

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_Init
    *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    *    形    参:无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_Init(void)
    {
        /* 配置MPU */
        MPU_Config();
        
        /* 使能L1 Cache */
        CPU_CACHE_Enable();
    
        /* 
           STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
           - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
           - 设置NVIV优先级分组为4。
         */
        HAL_Init();
    
        /* 
           配置系统时钟到400MHz
           - 切换使用HSE。
           - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
        */
        SystemClock_Config();
    
        /* 
           Event Recorder:
           - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
           - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第8章
        */    
    #if Enable_EventRecorder == 1  
        /* 初始化EventRecorder并开启 */
        EventRecorderInitialize(EventRecordAll, 1U);
        EventRecorderStart();
    #endif
        
        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
        bsp_InitTimer();      /* 初始化滴答定时器 */
        bsp_InitUart();    /* 初始化串口 */
        bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */    
        bsp_InitLed();        /* 初始化LED */    
    
        bsp_InitDAC();      /* 初始化DAC */
    
    }

      MPU配置和Cache配置:

    数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM),FMC的扩展IO区和D3域的SRAM4。DAC的数据缓存开在了SRAM4。

    /*
    *********************************************************************************************************
    *    函 数 名: MPU_Config
    *    功能说明: 配置MPU
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void MPU_Config( void )
    {
        MPU_Region_InitTypeDef MPU_InitStruct;
    
        /* 禁止 MPU */
        HAL_MPU_Disable();
    
        /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x24000000;
        MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        
        /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x60000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
        
        /* 配置SRAM4的MPU属性为Write through, read allocate,no write allocate */
        MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
        MPU_InitStruct.BaseAddress      = 0x38000000;
        MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
        MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
        MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
        MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
        MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
        MPU_InitStruct.Number           = MPU_REGION_NUMBER2;
        MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
        MPU_InitStruct.SubRegionDisable = 0x00;
        MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
        
        HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
        /*使能 MPU */
        HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: CPU_CACHE_Enable
    *    功能说明: 使能L1 Cache
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void CPU_CACHE_Enable(void)
    {
        /* 使能 I-Cache */
        SCB_EnableICache();
    
        /* 使能 D-Cache */
        SCB_EnableDCache();
    }

     主功能:

    主程序实现如下操作:

    •  启动1个500ms的自动重装定时器,让LED2每500ms翻转一次。
    •  PA4和PA5引脚输出100KHz的方波。
    /*
    *********************************************************************************************************
    *    函 数 名: main
    *    功能说明: c程序入口
    *    形    参: 无
    *    返 回 值: 错误代码(无需处理)
    *********************************************************************************************************
    */
    int main(void)
    {
        uint8_t ucKeyCode;        /* 按键代码 */
    
    #if defined ( __CC_ARM )    
        TempValues1 = 0; /* 避免MDK警告 */  
        TempValues2 = 0;    
    #endif    
        
        bsp_Init();        /* 硬件初始化 */
        
        PrintfLogo();    /* 打印例程名称和版本等信息 */
        PrintfHelp();    /* 打印操作提示 */
    
        bsp_StartAutoTimer(0, 500);    /* 启动1个500ms的自动重装的定时器 */
        
        /* 进入主程序循环体 */
        while (1)
        {
            bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    
            /* 判断定时器超时时间 */
            if (bsp_CheckTimer(0))    
            {
                /* 每隔100ms 进来一次 */  
                bsp_LedToggle(2);
            }
    
            /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
            ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
            if (ucKeyCode != KEY_NONE)
            {
                switch (ucKeyCode)
                {
                    case KEY_DOWN_K1:        /* K1键按下 */
                        printf("K1按键按下
    ");
                        break;
    
                    default:
                        /* 其它的键值不处理 */
                        break;
                }
            }
        }
    }

    60.9 总结

    本章节涉及到的知识点比较重要,以后用到DAC的地方也比较多,并且H7的DAC性能比较给力。

  • 相关阅读:
    1026 Table Tennis (30)
    1029 Median
    1025 PAT Ranking (25)
    1017 Queueing at Bank (25)
    1014 Waiting in Line (30)
    1057 Stack (30)
    1010 Radix (25)
    1008 Elevator (20)
    字母大小写转换
    Nmap的基础知识
  • 原文地址:https://www.cnblogs.com/armfly/p/12341626.html
Copyright © 2011-2022 走看看