原创 STM32笔记(三)ADC、DMA、USART的综合练习

2008-12-25 14:11 11191 9 17 分类: MCU/ 嵌入式
  这是一个综合的例子,演示了ADC模块、DMA模块和USART模块的基本使用。
  我们在这里设置ADC为连续转换模式,常规转换序列中有两路转换通道,分别是ADC_CH10(PC0)和ADC_CH16(片内温度传感器)。因为使用了自动多通道转换,数据的取出工作最适合使用DMA方式取出,so,我们在内存里开辟了一个u16 AD_Value[2]数组,并设置了相应的DMA模块,使ADC在每个通道转换结束后启动DMA传输,其缓冲区数据量为2个HalfWord,使两路通道的转换结果自动的分别落到AD_Value[0]和AD_Value[1]中。
  然后,在主函数里,就无需手动启动AD转换,等待转换结束,再取结果了。我们可以在主函数里随时取AD_Value中的数值,那里永远都是最新的AD转换结果。
  如果我们定义一个更大的AD_Value数组,并调整DMA的传输数据量(BufferSize)可以实现AD结果的循环队列存储,从而可以进行各种数字滤波算法。
  接着,取到转换结果后,根据V=(AD_Value/4096)*Vref+的公式可以算出相应通道的电压值,也可以根据  T(℃) =  (1.43 - Vad)/34*10^(-6) + 25的算法,得到片内温度传感器的测量温度值了。
  通过重新定义putchar函数,及包含"stdio.h"头文件,我们可以方便的使用标准C的库函数printf(),实现串口通信。
  相关的官方例程,可以参考FWLib V2.0的ADC\ADC1_DMA和USART\printf两个目录下的代码。

本代码例子是基于万利199的开发板EK-STM32F实现,CPU=STM32F103VBT6


/******************************************************************************
* 本文件实现ADC模块的基本功能
* 设置ADC1的常规转换序列包含CH10和CH16(片内温度传感器)
* 设置了连续转换模式,并使用DMA传输
* AD转换值被放在了AD_Value[2]数组内,[0]保存CH0结果,[1]保存CH16结果
* GetVolt函数计算[0]的值对应的电压值(放大100倍,保留2位小数)
* GetTemp函数计算[1]的值对应的温度值,计算公式在相应函数内有说明
作者:jjldc(九九)
*******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_lib.h"
#include "stdio.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_Address    ((u32)0x4001244C)
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
vu16 AD_Value[2];
vu16 i=0;
s16  Temp;
u16  Volt;

/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART1_Configuration(void);
void ADC1_Configuration(void);
void DMA_Configuration(void);

int fputc(int ch, FILE *f);
void Delay(void);
u16 GetTemp(u16 advalue);
u16 GetVolt(u16 advalue);
/* Private functions ---------------------------------------------------------*/
/*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
int main(void)
{
    RCC_Configuration();
    GPIO_Configuration();
    NVIC_Configuration();
    USART1_Configuration();
    DMA_Configuration();
    ADC1_Configuration();
   
    //启动第一次AD转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    //因为已经配置好了DMA,接下来AD自动连续转换,结果自动保存在AD_Value处  
   
    while (1)
    {
        Delay();
        Temp = GetTemp(AD_Value[1]);
        Volt = GetVolt(AD_Value[0]);
        USART_SendData(USART1, 0x0c);       //清屏
        //注意,USART_SendData函数不检查是否发送完成
        //等待发送完成
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

        printf("电压:%d.%d\t温度:%d.%d℃\r\n", \
            Volt/100, Volt%100, Temp/100, Temp%100);
       
    }
}

/*******************************************************************************
* Function Name  : 重定义系统putchar函数int fputc(int ch, FILE *f)
* Description    : 串口发一个字节
* Input          : int ch, FILE *f
* Output         :
* Return         : int ch
*******************************************************************************/
int fputc(int ch, FILE *f)
{
    //USART_SendData(USART1, (u8) ch);
    USART1->DR = (u8) ch;
   
    /* Loop until the end of transmission */
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }

    return ch;
}

/*******************************************************************************
* Function Name  : Delay
* Description    : 延时函数
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void Delay(void)
{
    u32 i;
    for(i=0;i<0x4f0000;i++);
    return;
}                           

/*******************************************************************************
* Function Name  : GetTemp
* Description    : 根据ADC结果计算温度
* Input          : u16 advalue
* Output         :
* Return         : u16 temp
*******************************************************************************/
u16 GetTemp(u16 advalue)
{
    u32 Vtemp_sensor;
    s32 Current_Temp;
   
//    ADC转换结束以后,读取ADC_DR寄存器中的结果,转换温度值计算公式如下:
//          V25 - VSENSE
//  T(℃) = ------------  + 25
//           Avg_Slope
//   V25:  温度传感器在25℃时 的输出电压,典型值1.43 V。
//  VSENSE:温度传感器的当前输出电压,与ADC_DR 寄存器中的结果ADC_ConvertedValue之间的转换关系为:
//            ADC_ConvertedValue * Vdd
//  VSENSE = --------------------------
//            Vdd_convert_value(0xFFF)
//  Avg_Slope:温度传感器输出电压和温度的关联参数,典型值4.3 mV/℃。

    Vtemp_sensor = advalue * 330 / 4096;
    Current_Temp = (s32)(143 - Vtemp_sensor)*10000/43 + 2500;
    return (s16)Current_Temp;



/*******************************************************************************
* Function Name  : GetVolt
* Description    : 根据ADC结果计算电压
* Input          : u16 advalue
* Output         :
* Return         : u16 temp
*******************************************************************************/
u16 GetVolt(u16 advalue)
{
    return (u16)(advalue * 330 / 4096);
}
                         

/*******************************************************************************
* Function Name  : RCC_Configuration
* Description    : 系统时钟设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void RCC_Configuration(void)
{
    ErrorStatus HSEStartUpStatus;

    //使能外部晶振
    RCC_HSEConfig(RCC_HSE_ON);
    //等待外部晶振稳定
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
    //如果外部晶振启动成功,则进行下一步操作
    if(HSEStartUpStatus==SUCCESS)
    {
        //设置HCLK(AHB时钟)=SYSCLK
        RCC_HCLKConfig(RCC_SYSCLK_Div1);

        //PCLK1(APB1) = HCLK/2
        RCC_PCLK1Config(RCC_HCLK_Div2);

        //PCLK2(APB2) = HCLK
        RCC_PCLK2Config(RCC_HCLK_Div1);
       
        //设置ADC时钟频率
        RCC_ADCCLKConfig(RCC_PCLK2_Div2);

        //FLASH时序控制
        //推荐值:SYSCLK = 0~24MHz   Latency=0
        //        SYSCLK = 24~48MHz  Latency=1
        //        SYSCLK = 48~72MHz  Latency=2
        FLASH_SetLatency(FLASH_Latency_2);
        //开启FLASH预取指功能
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

        //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
        //启动PLL
        RCC_PLLCmd(ENABLE);
        //等待PLL稳定
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        //系统时钟SYSCLK来自PLL输出
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        //切换时钟后等待系统时钟稳定
        while(RCC_GetSYSCLKSource()!=0x08);

       
    }

    //下面是给各模块开启时钟
    //启动GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | \
                           RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD,\
                           ENABLE);
    //启动AFIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //启动USART1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //启动DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    //启动ADC1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

}


/*******************************************************************************
* Function Name  : GPIO_Configuration
* Description    : GPIO设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    //PC口4567脚设置GPIO输出,推挽 2M
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    //KEY2 KEY3 JOYKEY
    //位于PD口的3 4 11-15脚,使能设置为输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_11 | GPIO_Pin_12 |\
        GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    //USART1_TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //ADC_CH10--> PC0
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

}



/*******************************************************************************
* Function Name  : NVIC_Configuration
* Description    : NVIC设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

#ifdef  VECT_TAB_RAM
    // Set the Vector Table base location at 0x20000000
    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else  /* VECT_TAB_FLASH  */
    // Set the Vector Table base location at 0x08000000
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif

    //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    //串口中断打开
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


/*******************************************************************************
* Function Name  : USART1_Configuration
* Description    : NUSART1设置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USART1_Configuration(void)
{
    USART_InitTypeDef USART_InitStructure;
   
    USART_InitStructure.USART_BaudRate = 19200;
    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_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);
   
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
   
    USART_Cmd(USART1, ENABLE);
}

/*******************************************************************************
* Function Name  : ADC1_Configuration
* Description    : ADC1设置(包括ADC模块配置和自校准)
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void ADC1_Configuration(void)
{
    ADC_InitTypeDef ADC_InitStructure;

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;  //连续转换开启
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 2;     //设置转换序列长度为2
    ADC_Init(ADC1, &ADC_InitStructure);
   
    //ADC内置温度传感器使能(要使用片内温度传感器,切忌要开启它)
    ADC_TempSensorVrefintCmd(ENABLE);
   
    //常规转换序列1:通道10
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5);
    //常规转换序列2:通道16(内部温度传感器),采样时间>2.2us,(239cycles)
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5);
   
    // Enable ADC1
    ADC_Cmd(ADC1, ENABLE);
    // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
    ADC_DMACmd(ADC1, ENABLE);
   
    // 下面是ADC自动校准,开机后需执行一次,保证精度
    // Enable ADC1 reset calibaration register
    ADC_ResetCalibration(ADC1);
    // Check the end of ADC1 reset calibration register
    while(ADC_GetResetCalibrationStatus(ADC1));

    // Start ADC1 calibaration
    ADC_StartCalibration(ADC1);
    // Check the end of ADC1 calibration
    while(ADC_GetCalibrationStatus(ADC1));
    // ADC自动校准结束---------------
   
}

/*******************************************************************************
* Function Name  : DMA_Configuration
* Description    : DMA设置:从ADC模块自动读转换结果至内存
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
   
    DMA_DeInit(DMA1_Channel1);
    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    //BufferSize=2,因为ADC转换序列有2个通道
    //如此设置,使序列1结果放在AD_Value[0],序列2结果放在AD_Value[1]
    DMA_InitStructure.DMA_BufferSize = 2;
    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;
    //循环模式开启,Buffer写满后,自动回到初始地址开始传输
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    //配置完成后,启动DMA通道
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

PARTNER CONTENT

文章评论8条评论)

登录后参与讨论

用户1588142 2012-3-26 20:46

mark

用户330060 2011-5-4 17:10

博主帮帮忙啊,弄了好几天了。。。找不到原因。。。。。

用户330060 2011-5-4 17:09

我想实现ADC双通道的电压检测,用了一个数组val【】,可是设置之后,val【1】始终为0,val【0】却是一个变化的数,这是我写的配置,请问问题出现在哪里? void DMA_channel1configuration(void) { /* DMA channel1 configuration ----------------------------------------------*/ DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC_ConvertedValue_1; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 2; 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_Channel1, &DMA_InitStructure); /* Enable DMA channel1 */ DMA_Cmd(DMA1_Channel1, ENABLE); } void ADC1_configuration(void) { /* ADC1 configuration ------------------------------------------------------*/ ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 2; ADC_Init(ADC1, &ADC_InitStructure); ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_55Cycles5); /* Enable ADC1 DMA */ ADC_DMACmd(ADC1, ENABLE); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* Enable ADC1 reset calibaration register */ ADC_ResetCalibration(ADC1); /* Check the end of ADC1 reset calibration register */ while(ADC_GetResetCalibrationStatus(ADC1)); /* Start ADC1 calibaration */ ADC_StartCalibration(ADC1); /* Check the end of ADC1 calibration */ while(ADC_GetCalibrationStatus(ADC1)); /* Start ADC1 Software Conversion */ ADC_SoftwareStartConvCmd(ADC1, ENABLE); }

用户1624476 2010-9-2 10:49

编译时显示FILE未定义,这是为什么啊!请博主帮助一下

用户1517868 2010-8-7 10:40

谢谢,支持

用户205554 2009-9-24 14:45

很清晰!

用户136065 2009-2-9 13:22

写的很好

用户1572896 2009-1-12 16:23

很好,支持一下
相关推荐阅读
用户699237 2009-06-04 14:36
发布TBDML Plus用户手册和驱动包
九九的TBDML Plus已经于2009年5月1日正式发售在EDN和21IC上发布TBDML Plus用户使用手册和用户驱动包欢迎各位用户下载使用TBDML Plus用户包下载:https://sta...
用户699237 2009-05-30 16:39
低价供应Freescale飞思卡尔 BDM仿真器 调试器 兼容8/16/32位单片机
本店最新产品的飞思卡尔三合一BDM仿真器TBDML Plus支持S12 S08 ColdFireV1三种内核的仿真调试USB接口 最新JB16+12MHz晶振硬件 仿真速度是原BDM的2倍!超小体积 ...
用户699237 2009-05-20 15:12
隆重发布高速模拟视频解码模块(智能车可用)
关于智能车摄像头的选择,CCD以其高感光度、能在运动时获取较为清晰的图像而成为智能车摄像头的首选,这是一个趋势。详情请搜索《CCD与CMOS摄像头在智能车竞赛中的选择》  同时摄像头的前瞻越来越大,很...
用户699237 2009-05-15 16:45
智能车竞赛中CCD与CMOS摄像头的选择
https://static.assets-stash.eet-china.com/album/old-resources/2009/5/15/3cb54ae1-ee52-4f05-9629-86c2...
用户699237 2009-05-12 21:03
智能车上位机:串口图像显示程序
这是我很久以前写的程序功能很不完善如果要使用 请先把串口调整到COM1 不能在设置窗口中设置串口号 否则会出错同时连拍功能没有实现 嘿嘿 不好意思哦具体如何发送 在readme中有详细解释没空改程序了...
用户699237 2009-05-12 21:02
OV7620 OV6620等CMOS摄像头图像采集的方法
本文由九九原创,发表于《电子技术应用》2008年第9期,版权归作者与出版社所有,转载请务必注明作者出处,谢谢http://shop35388432.taobao.com九九小铺,供应:MC9S12DG...
EE直播间
更多
我要评论
8
9
关闭 站长推荐上一条 /1 下一条