黑海里海地图:stm32使用体会

来源:百度文库 编辑:偶看新闻 时间:2024/04/28 10:46:35
本人备注:以下这篇文章,对于本人圆机项目某方面有比较大的参考意义。其PWM转直流电平的经验之谈也值得借鉴!~

       第一次使用stm32,以前用过51、avr、pic、2812,感觉stm32还真有点不一样,呵呵。

因为是第一次使用,下面说的肯定有不少错误,诚心求大家指正。

       这次做的是用stm32f103zd+lattice 的lc4256v做一个波形发生器。通过上位机可以控制生成波形的频率,然后stm32根据频率计算波形占空比数据,通过总线形式传给cpld,然后 cpld把这些数据转换成相对应占空比的pwm输出,外部接RC滤波电路,产生相对应的波形。由于频率范围较大,计算量也比较大,所以采用了 stm32+cpld的结构。Stm32运行在72MHZ,通过mco脚给cpld 36M HZ的时钟,stm32和cpld通过总线方式通信。

       此系统中Stm32主要用到的资源是:一个UART,一个TIMER及其中断,FSMC和DMA。

       本人总结了下,Stm32初始化一个片内外设一般过程一般有以下几部分:

1.  InitStructure配置及初始化

2.  时钟使能

3.  相对应的IOInitStructure配置及初始化

4.  相对应的IO时钟使能

5.  外设使能

6.  中断配置及中断程序编写

下面介绍一下自己所用的UART、TIMER、FSMC、DMA的初始化。

UART初始化:

       此系统中使用的是UART2,未用UART中断。UART初始化主要有:IO初始化,UART InitSturcture初始化,UART时钟使能,UART使能。程序如下:

GPIO_InitTypeDef GPIO_InitStructure;

// Configure USART2_Tx as alternate push-pull

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

 

    // Configure USART2_Rx as input floating

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA, &GPIO_InitStructure);

//IO时钟使能

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

USART_InitTypeDef USART_InitStructure;

 

  USART_InitStructure.USART_BaudRate = 9600;

  USART_InitStructure.USART_WordLength = USART_WordLength_8b;

  USART_InitStructure.USART_StopBits = USART_StopBits_1;

  USART_InitStructure.USART_Parity = USART_Parity_No ;

  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

 

 

  USART_Init(USART2, &USART_InitStructure); 

//UART时钟使能 

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

 

  USART_Cmd(USART2, ENABLE);

TIMER初始化

       使用的是TIM2。初始化主要包括TIM2 InitSturcture初始化,时钟使能,TIM2开启,中断配置,及中断服务程序编写。此TIM2作用主要是给DMA提供时钟,DMA在TIM2 UP时启动一次DMA发送过程。TIM2程序如下:

       TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

       //定时器2

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

  TIM_TimeBaseStructure.TIM_Period = 33;

  TIM_TimeBaseStructure.TIM_Prescaler = 71;

  TIM_TimeBaseStructure.TIM_ClockDivision = 0;

  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0;

  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

  TIM_ClearFlag(TIM2,TIM_FLAG_Update);

  TIM_ARRPreloadConfig(TIM2,ENABLE);

  TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

  TIM_Cmd(TIM2, ENABLE);

//中断配置

 

  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQChannel;

  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStructure);

//定时器2中断服务程序

void TIM2_IRQHandler(void)

{

  TIM_ClearITPendingBit(TIM2,TIM_IT_Update);

}

FSMC初始化

       FSMC主要用来和CPLD进行总线通信,由DMA方式发送,在TIM2计时时间到后启动一次DMA发送,发送的数据由已计算好的数组中的一个16位数据以16位方式发给FSMC的地址。由于此系统电路已固定,stm32与CPLD间数据线是8位,故在向FSMC地址写16位数据时,FSMC会将数据拆成 2部分发送。本人在实际编程时发现,如向*(volatile u16*)(Bank1_NOR4_ADDR+0x40)地址给CPLD写16位数据时,会在40h接收到低8位数据,在41h接收到高8位数据。按道理来说这些数据应该与NBL0, NBL1信号有关,本人在CPLD编程时未理会这个,具体时序不是很清楚,有待考究。

       FSMC初始化程序如下:

FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;

       FSMC_NORSRAMTimingInitTypeDef  p;

       p.FSMC_AddressSetupTime = 3;  //6

       p.FSMC_AddressHoldTime = 0;     //3

       p.FSMC_DataSetupTime = 8;  //25

       p.FSMC_BusTurnAroundDuration = 0;

       p.FSMC_CLKDivision = 0;

       p.FSMC_DataLatency = 0;

       p.FSMC_AccessMode = FSMC_AccessMode_A;

      

       FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;

       FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;

       FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_SRAM;

       FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;

       FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;

       FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;

       FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;

       FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;

       FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;

       FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;

       FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;

       FSMC_NORSRAMInitStructure.FSMC_AsyncWait = FSMC_AsyncWait_Disable;

       FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;

       FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;

       FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;

      

       FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 

       RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);

       FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);

//IO初始化

GPIO_InitTypeDef GPIO_InitStructure;

 

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOG | RCC_APB2Periph_GPIOE |

                         RCC_APB2Periph_GPIOF, ENABLE);

 

 

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_8 | GPIO_Pin_9 |

                                GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOD, &GPIO_InitStructure);

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |

                                GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 |

                                GPIO_Pin_15;

  GPIO_Init(GPIOE, &GPIO_InitStructure);

 

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |

                                GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_12 | GPIO_Pin_13 |

                                GPIO_Pin_14 | GPIO_Pin_15;

  GPIO_Init(GPIOF, &GPIO_InitStructure);

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |

                                GPIO_Pin_4 | GPIO_Pin_5;

  GPIO_Init(GPIOG, &GPIO_InitStructure);

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;

  GPIO_Init(GPIOD, &GPIO_InitStructure);

  

   

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 |GPIO_Pin_5;

  GPIO_Init(GPIOD, &GPIO_InitStructure);

 

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;

  GPIO_Init(GPIOG, &GPIO_InitStructure);

 

 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;

  GPIO_Init(GPIOE, &GPIO_InitStructure);

此处简单介绍一下FSMC总线方式的使用。

       FSMC初始化完了之后,进行如下定义,

#define Bank1_NOR4_ADDR       ((u32)0x6c000000)

#define cs_sin  *(volatile u16*)(Bank1_NOR4_ADDR+0x40)

然后cs_sin=1000就是往这个地址写数字1000=0x03e8,则cpld 40h地址收到数据为0xe8,41h收到的数据为0x03

i=cs_sin,就是读这个地址的数据,由于定义的是16位的数据地址,故读到的数据是40h为低8位数据,41h为高8位数据

DMA初始化:

       DMA在TIM2 UP时触发,将已经计算好放在数组dat_tocpld的16位数据发送到fsmc地址为(Bank1_NOR4_ADDR+0x40)的空间。

       初始化程序如下:

DMA_InitTypeDef DMA_InitStructure;

  DMA_DeInit(DMA1_Channel2);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

 

  TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);

 

  DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(Bank1_NOR4_ADDR+0x40);

  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)dat_tocpld;

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

  DMA_InitStructure.DMA_BufferSize = 1152;

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

  DMA_InitStructure.DMA_PeripheralDataSize =  DMA_PeripheralDataSize_HalfWord;

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;

  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  DMA_Init(DMA1_Channel2, &DMA_InitStructure);

 

DMA_Cmd(DMA1_Channel2, ENABLE);

  最后附上源自http://sxqstudy.blog.163.com/blog/static/34562512009625103148709/介绍的关于PWM电压转换电路。

 

在PWM用于DA转换的场合,阻容滤波电路是关系转换效果的重要环节。

由RC充放电常数我们可以大致计算出阻容环节的充放电频率,一般为了得到理想的滤波效果,这个频率要远小于PWM的输出频率(小于四分之一)。

一般情况下,当C较小R较大时,DA转换出的电压损耗很小,但是纹波却很大;当C较大R较小时,DA转换出的电压损耗很大,但纹波相对较小。

所以当需要进行线形度很高的精确DA转换时必须使用较小的滤波电容,且尽量避免使用电解类电容。而为了得到较强的信号输出,RC惯性环节之后还必须加一级高性能的电压跟随,然后在跟随器输出的地方加上一个滤波用的电解电容,用于平滑RC惯性环节的纹波。但是这还不够,因为这时的输出电压里可能含有较多的交流谐波成分,如果处理不当,跟随器有可能自激。解决的办法就是使用一个小的去藕电容。而且这里电容的放置顺序必须是电解电容在前,去藕电容在后!

如果输出电压精度和线形度要求不高,但是对纹波要求却很高,或者这个电压比较固定时,可以使用电容较大的滤波组合。因为,虽然大电容的直流损耗较大,但是我们可以通过调节PWM占空比来达到要求的输出电压,或者通过一级AD转换的反馈来实现精确的固定电压输出。只是这里仍然要加一级电压跟随器,以便于后级采集电路使用,且AD采集点放置在跟随器输出处。