0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用STM32CubeMX生成初始化代码

CHANBAEK 来源:头条号科G栈 作者:头条号科G栈 2023-03-22 15:26 次阅读

我使用STM32CubeMX生成初始化代码,使用LL库,这里只介绍跟i2c相关的部分,其他必要的初始化需要自己完成。芯片使用stm32f042。本文的代码不能到手即用,只提供思路。

1、初始化

初始化部分包括GPIO、DMA、I2C等。

1、GPIO

这部分自动生成就OK,一般不需要作修改;

LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  /**I2C1 GPIO Configuration
  PA9   ------> I2C1_SCL
  PA10   ------> I2C1_SDA
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

2、DMA

DMA的初始化自动生成的程序会分为两个部分:

第一个部分如下,会打开时钟、初始化中断:

void MX_DMA_Init(void)
{

  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);

  /* DMA interrupt init */
    /* DMA1_Channel2_3_IRQn interrupt configuration */
  NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
  NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);

}

第二部分在I2C的初始化程序中

LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_HIGH);

LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_NORMAL);

LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);

LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);

LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);

LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);
//上面是自动生成的,下面的部分需要自己添加
LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,5);
LL_DMA_SetMemoryAddress(DMA1,LL_DMA_CHANNEL_3,(uint32_t)i2cDataRx);
LL_DMA_SetPeriphAddress(DMA1,LL_DMA_CHANNEL_3,LL_I2C_DMA_GetRegAddr(I2C1,LL_I2C_DMA_REG_DATA_RECEIVE));  
LL_DMA_EnableIT_TC(DMA1,LL_DMA_CHANNEL_3);

自动生成程序会完成DMA的如下设置:

  • 数据传输方向
  • 通道极性
  • 模式
  • 外设地址模式
  • 内存地址模式
  • 外设数据大小
  • 内存数据大小

我们需要自己添加:

  • 传输数据个数
  • 设置内存地址
  • 设置外设地址
  • 打开中断,根据需要选择传输完成、传输一半和传输错误

DMA的模式有两种:NORMAL和CIRCULAR。

CIRCULAR模式一旦开始传输,DMA控制器就会自动不停的从源地址拿数据发送到目的地址,不需要我们干预。由于是异步的,如果内存的数据多于1个,有可能出现内存数据一部分新一部分旧的情况,导致数据不同步,如果各个数据之间独立还好,如果是一个整体就会出问题,所以要根据实际需求决定是否使用这种方式。

NORMAL模式发送一次后就停止了,如果还要发送就需要我们先关闭DMA通道,设置传输的数据个数,再打开通道,循环往复。本例使用这种方式,在中断中处理三个过程,稍后介绍。

3、I2C

I2C包括时钟、中断、地址、时钟、模式等等

/* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
  /* I2C1 interrupt Init */
  NVIC_SetPriority(I2C1_IRQn, 0);
  NVIC_EnableIRQ(I2C1_IRQn);
  /* USER CODE BEGIN I2C1_Init 1 */  
  /* USER CODE END I2C1_Init 1 */
  /** I2C Initialization
  */  
  LL_I2C_DisableGeneralCall(I2C1);
  LL_I2C_EnableClockStretching(I2C1);
  I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  I2C_InitStruct.Timing = 0x2000090E;
  I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
  I2C_InitStruct.DigitalFilter = 0;
  I2C_InitStruct.OwnAddress1 = 0x5A;
  I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  LL_I2C_Init(I2C1, &I2C_InitStruct);
  LL_I2C_EnableAutoEndMode(I2C1);
  LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
  LL_I2C_EnableOwnAddress2(I2C1);
//上面的部分是自动生成的,下面是自己添加的
    /* USER CODE BEGIN I2C1_Init 2 */
    LL_I2C_Enable(I2C1);
    LL_I2C_EnableIT_ADDR(I2C1);
   // LL_I2C_EnableIT_ERR(I2C1);
    LL_I2C_EnableDMAReq_RX(I2C1);
    LL_I2C_EnableDMAReq_TX(I2C1);

地址必须是偶数,这里使用了双地址,地址2是0,这样0和5A都可以通信

I2C在通信过程中会产生很多中断,比如地址匹配、NACK、STOP、错误、溢出等等,这里根据需要只开启地址匹配中断(ADDR),一旦检测到地址匹配,我们就开启DMA传输数据,其他的事情交给DMA处理。

最后两个分别是启用 DMA 接收请求和启用 DMA 发送请求,只有开启它们DMA和I2C才能关联上。

到此初始化基本完成。

2、中断处理程序

1、I2C中断处理程序

这里就判断是否地址匹配,如果匹配,判断是读还是写,这里读写以主机视角确定,如果是WRITE,说明从机此时要接收数据。(这里我发现不同的版本和系列定义的还不一样,使用的时候要注意。)

void I2C1_IRQHandler(void)
{
  /* USER CODE BEGIN I2C1_IRQn 0 */
    if(LL_I2C_IsActiveFlag_ADDR(I2C1))
    {
        if(LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_WRITE)
        {
            LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_3);
        }
        else
        { 
            LL_DMA_EnableChannel(DMA1,LL_DMA_CHANNEL_2);  
        }
        /* Clear ADDR flag value in ISR register */
        LL_I2C_ClearFlag_ADDR(I2C1);
    }   
  /* USER CODE END I2C1_IRQn 0 */

  /* USER CODE BEGIN I2C1_IRQn 1 */

  /* USER CODE END I2C1_IRQn 1 */
}

这里根据方向开启对应的DMA通道,清除ADDR标志,之后数据就自动通过DMA传输了。

2、DMA中断处理程序

这里由于通道2和3公用一个中断,所以要先判断是谁触发的中断,然后清除对应的中断标志。前面我们设置的是DMA传输完成中断,所以进入这里就表面数据传完了。由于我们使用的是NORMAL模式,所以我在这个回调里关闭通道并重设传输的数据个数。我之所以放到这里是考虑到传输玩数据后一般会有个间隔,这段时间没事干就处理一下这些必要的事情,等下次想要传输的时候直接打开就行。(前面在i2c中断程序里我们可以看到打开通道的代码。)你要是无所谓都放到I2C的中断里也可以的。

void DMA1_Channel2_3_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 0 */
    if(LL_DMA_IsActiveFlag_TC3(DMA1))
    {
        //LL_DMA_ClearFlag_GI3(DMA1);
        LL_DMA_ClearFlag_TC3(DMA1);
        
        I2C_SlaveDMARxCpltCallback();  
        LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_3);
        LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_3,5);      
    }
    else if (LL_DMA_IsActiveFlag_TC2(DMA1))
    {
        LL_DMA_ClearFlag_TC2(DMA1);
        LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_2);    
        LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_2,5);
    }
    
  /* USER CODE END DMA1_Channel2_3_IRQn 0 */

  /* USER CODE BEGIN DMA1_Channel2_3_IRQn 1 */

  /* USER CODE END DMA1_Channel2_3_IRQn 1 */
}

在接收中断中有一个回调函数

I2C_SlaveDMARxCpltCallback(),里边是用户自定义程序,你想收到数据干啥就可以在这里边处理。

剩下所要做的事情就是准备好要发送的数据和使用收到的数据就行了。

最近发现使用DMA真的很方便,尤其在发送或接收多个数据的时候,就不用for循环了,这样既能收发大量数据,还不会占用CPU时间,效率大大提高。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • I2C
    I2C
    +关注

    关注

    28

    文章

    1346

    浏览量

    120804
  • dma
    dma
    +关注

    关注

    3

    文章

    535

    浏览量

    99011
  • 代码
    +关注

    关注

    30

    文章

    4554

    浏览量

    66734
  • GPIO
    +关注

    关注

    16

    文章

    1132

    浏览量

    50560
  • stm32cubemx
    +关注

    关注

    5

    文章

    260

    浏览量

    14446
收藏 人收藏

    评论

    相关推荐

    STM32CubeMx入门教程(3):定时器的使用

    导语“ 使用STM32CubeMx 生成初始化定时器代码,实现定时器特定定时”
    发表于 07-11 15:22 2301次阅读
    <b class='flag-5'>STM32CubeMx</b>入门教程(3):定时器的使用

    STM32CubeMx入门教程(4):PWM应用

    导语“使用STM32CubeMx配置定时器输出PWM波,生成初始化代码。”
    发表于 07-11 15:23 902次阅读
    <b class='flag-5'>STM32CubeMx</b>入门教程(4):PWM应用

    STM32接入PC后能枚举出来麦克风设备,但是开始录音后无数据,为什么?

    采集MEMS数字麦克风PDM数据,然后调用PDM2PCM库转换为PCM数据,最后把PCM数据通过USB上传给PC实现PC录音使用的STM32CubeMx生成初始化代码后默认是只有播放设
    发表于 03-18 08:25

    【NUCLEO-F412ZG试用体验】一、USART+LED+KEY

    USART+LED+KEY 开始。首先说明,使用 STM32CubeMx 生成初始化代码工程,使用 HAL 库编写测试程序。1、打开 STM32C
    发表于 11-28 12:26

    【NUCLEO-F412ZG试用体验】FreeRTOS创建任务

    。算了,还是改为 4 个 FreeRTOS测试吧。首先说明,本例程使用 STM32CubeMx 生成初始化代码工程,使用 HAL库编写测试程序。实验现象:1、3 个LED闪烁2、按下按
    发表于 12-13 21:00

    STM32CubeMX与HAL库进行开发

    STM32CubeMX与HAL库学习--基本定时器定时中断背景STM32CubeMX生成初始化代码在MDK-ARM里编辑
    发表于 08-11 06:59

    使用STM32CubeMX进行硬件电路的初始化配置

    目录目的工具版本使用方法时钟配置SYS配置GPIO配置USB配置Clock Configuration生成工程代码简单代码测试目的本文介绍使用STM32CubeMX进行硬件电路的
    发表于 08-11 06:43

    使用STM32CubeMX生成的时钟初始化函数

    \GPIO_IOToggle\MDK-ARM3、由于板子默认支持SMPS,不直接支持LDO,故使用STM32CubeMX生成的时钟初始化函数中,需要进行如下替换:HAL_PWREx_ConfigSupply(PWR_LDO_SU
    发表于 08-18 06:32

    怎样使用STM32CubeMx生成FMC的初始化代码

    W9825G6KH-6的原理是怎样的?怎样使用STM32CubeMx生成FMC的初始化代码呢?
    发表于 10-18 09:52

    如何利用STM32CubeMX生成STM32F429初始化代码

    如何利用STM32CubeMX生成STM32F429初始化代码呢?
    发表于 12-01 07:10

    TIM生成PWM的方法

    STM32CubeMX与HAL库学习--TIM生成PWM的简单练习前言STM32CubeMX生成初始化
    发表于 01-12 07:21

    STM32CubeMX外设在哪里初始化

    生成了 MX init 函数,但在示例项目 STM32CubeMX 中配置为不生成函数调用。我找到了初始化ADC和GPIO引脚的地方,但是,我找不到
    发表于 01-11 06:28

    STM32F4 USB麦克风录音后无数据,BUSHOUN数据长度一直是0无法进入函数USBD_AUDIO_DataIn怎么解决

    采集MEMS数字麦克风PDM数据,然后调用PDM2PCM库转换为PCM数据,最后把PCM数据通过USB上传给PC实现PC录音 使用的STM32CubeMx生成初始化代码后默认是只有播放
    发表于 08-07 08:16

    STM32CubeMX与HAL库学习--ADC与USART的简单练习

    STM32CubeMX与HAL库学习--ADC与USART的简单练习前言STM32CubeMX生成初始化代码在MDK-ARM里编辑
    发表于 01-12 20:27 13次下载
    <b class='flag-5'>STM32CubeMX</b>与HAL库学习--ADC与USART的简单练习

    STM32CubeMX用于STM32配置并初始化C代码生成

    STM32CubeMX用于STM32配置 并初始化C代码生成.STM32CubeMX
    发表于 02-14 11:00 5次下载