打印

使用体会

[复制链接]
1457|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
alenwei|  楼主 | 2012-7-18 23:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
pi, IO, gp, GPIO, ST
第一次使用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采集点放置在跟随器输出处。
沙发
elec921| | 2012-7-19 08:19 | 只看该作者
感谢分享

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

57

帖子

0

粉丝