STM32基础设计(5)---ADC转换(中断方式)

原文地址::https://blog.csdn.net/aa867734398/article/details/79710152

相关文章

1、怎样用STM32 ADC测量电压(中断方式)----https://blog.csdn.net/rannar/article/details/81154765

2、STM32F103VET6——ADC单通道中断读取实验讲解----https://blog.csdn.net/weixin_41454993/article/details/82081239

 

本文简单介绍了STM32F103C8,通过中断方式读取电压,不过最后楼主读取参考电压失败,还没有找到错误,所以读取的电压只能十六进制显示,如有不便请忽略本文!

本文的介绍按照一般流程来走:

1,串口的初始化

2,ADC初始化

3,中断初始化

4,编写中断函数

5,编写主函数

接下来详细介绍:

1,串口的初始化:
void usart_init()
{
     GPIO_InitTypeDef Uart_A;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);  
    Uart_A.GPIO_Pin = GPIO_Pin_9;  
    Uart_A.GPIO_Speed = GPIO_Speed_50MHz;  
    Uart_A.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_Init(GPIOA,&Uart_A);  
      
    Uart_A.GPIO_Pin = GPIO_Pin_10;  
    Uart_A.GPIO_Speed = GPIO_Speed_50MHz;  
    Uart_A.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
    GPIO_Init(GPIOA,&Uart_A);   
    
    USART_InitTypeDef Uart;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);  
    Uart.USART_BaudRate = 115200;  
    Uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
    Uart.USART_Mode = USART_Mode_Tx;  
    Uart.USART_Parity = USART_Parity_No;  
    Uart.USART_StopBits = USART_StopBits_1;  
    Uart.USART_WordLength = USART_WordLength_8b;  
    USART_Init(USART1,&Uart);  
      
    USART_Cmd(USART1,ENABLE);  
    USART_ClearFlag(USART1,USART_FLAG_TC); 
}
关于本段代码,我前面写的文章STM32基础设计(3)有详细讲解,此处不再赘述。

2,ADC初始化
typedef struct
{//配置ADC的模式,一个ADC是独立模式,2个是双模式
  uint32_t ADC_Mode;                      /*!< Configures the ADC to operate in independent or
                                               dual mode. 
                                               This parameter can be a value of @ref ADC_mode */
//配置ADC是否使用扫描,单通道不扫描,多通道扫描
  FunctionalState ADC_ScanConvMode;       /*!< Specifies whether the conversion is performed in
                                               Scan (multichannels) or Single (one channel) mode.
                                               This parameter can be set to ENABLE or DISABLE */
//配置ADC是单次转换还是连续转换
  FunctionalState ADC_ContinuousConvMode; /*!< Specifies whether the conversion is performed in
                                               Continuous or Single mode.
                                               This parameter can be set to ENABLE or DISABLE. */
//外部触发选择
  uint32_t ADC_ExternalTrigConv;          /*!< Defines the external trigger used to start the analog
                                               to digital conversion of regular channels. This parameter
                                               can be a value of @ref ADC_external_trigger_sources_for_regular_channels_conversion */
//转换结果数据对其方式
  uint32_t ADC_DataAlign;                 /*!< Specifies whether the ADC data alignment is left or right.
                                               This parameter can be a value of @ref ADC_data_align */
//ADC转换的通道数目
  uint8_t ADC_NbrOfChannel;               /*!< Specifies the number of ADC channels that will be converted
                                               using the sequencer for regular channel group.
                                               This parameter must range from 1 to 16. */
}ADC_InitTypeDef;
下面粘贴代码:

void Adc_Init()
{
    ADC_InitTypeDef adc;//定义ADC结构的变量
    GPIO_InitTypeDef io_b;//定义串口结构体变量 ,开发板上的电源接的是GPIOB端口的 1引脚,查数据手册,其为ADC1的9通道
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB,ENABLE);//开时钟(即把心脏激活)
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
    io_b.GPIO_Pin = GPIO_Pin_1;//设置端口引脚为 引脚1
    io_b.GPIO_Mode = GPIO_Mode_AIN;//设置为输入模式
    io_b.GPIO_Speed = GPIO_Speed_50MHz;//最高速率为50MHz
    GPIO_Init(GPIOB,&io_b);初始化引脚
    ADC_DeInit(ADC1);先将外设ADC1的全部寄存器重设为默认值 ADC_TempSensorVrefintCmd(ENABLE);谁能外部参照电压(勿忘)
    adc.ADC_Mode = ADC_Mode_Independent;设置ADC为独立模式
    adc.ADC_ScanConvMode = ENABLE;使能扫描模式
    adc.ADC_ContinuousConvMode = ENABLE;使能连续扫描
    adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;不使用外部触发
    adc.ADC_DataAlign = ADC_DataAlign_Right;数据右对齐
    adc.ADC_NbrOfChannel = 2;来制定用几个ADC通达(勿忘)
    ADC_Init(ADC1,&adc);初始化ADC寄存器
    
    ADC_RegularChannelConfig(ADC1,ADC_Channel_9,1,ADC_SampleTime_239Cycles5);制定用哪个ADC转换、第几个通道,转换的顺序、转换的周期
    ADC_RegularChannelConfig(ADC1,ADC_Channel_17,2,ADC_SampleTime_239Cycles5);
    //这里需要根据数据手册来设定通道,数据手册上会说明那个引脚对应那个通道,外接电源接到那个引脚上就可以了,必须按照数据手册的要求来,不然肯定会出错,博主在这里就有一个很大很大的教训,望读者谨记
    ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);打开ADC中断
    ADC_Cmd(ADC1,ENABLE);使能ADC1
    ADC_ResetCalibration(ADC1);复位ADC1的校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));等待校准寄存器复位完成
    
    ADC_StartCalibration(ADC1);开始ADC1校准
    while(ADC_GetCalibrationStatus(ADC1));等待ADC1校准完成
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);使能指定的ADC1的软件转换启动功能
}
3.中断初始化
void adc_nvic_init()
{
    
    NVIC_InitTypeDef nvic;定义中断结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);设置中断分组
    nvic.NVIC_IRQChannel = ADC1_2_IRQn;制定专断通道
    nvic.NVIC_IRQChannelCmd = ENABLE;使能中断
    nvic.NVIC_IRQChannelPreemptionPriority = 1;抢占优先级
    nvic.NVIC_IRQChannelSubPriority = 1;子优先级
    NVIC_Init(&nvic);初始化
    
    NVIC_InitTypeDef usart1;定义中断结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);设置中断分组
    usart1.NVIC_IRQChannel = USART1_IRQn;指定中断通道
    usart1.NVIC_IRQChannelCmd = ENABLE;设能中断通道
    usart1.NVIC_IRQChannelPreemptionPriority = 0;抢占优先级
    usart1.NVIC_IRQChannelSubPriority= 0;子优先级
    NVIC_Init(&usart1);中断初始化
}
博主在这里遇到一个问题,该问题时,中断分组的设置,上面两段程序得分别设置中断分组,要不然会有一个中断不会执行(没有再次设置中断分组的那个),不知道是博主代码规范的问题还是就必须这样设置。博主还未搞懂,如果读者有知道这个问题答案的,请在评论里赐教,不胜感激。

4,编写中断函数
先粘贴代码:

void ADC1_2_IRQHandler()
{
    if(ADC_GetITStatus(ADC1,ADC_IT_EOC) == SET)这里使用来判断,如果已经转换完成EOC位为1,具体请查看参考手册ADC状态寄存器ADC_SR的eEOC位
    {if(count_1%2 == 0){代码中的if语句中的嵌套if语句是用来区分外接电源电压和内部参考电压的。具体见下面的解释
        ADC_ConvertedValue = ADC_GetConversionValue(ADC1);
        count_1++;
    }else
            {
                ADC_ConvertedValue_1 = ADC_GetConversionValue(ADC1);
                //ADC_ConvertedValue_1 = ADC1->DR;
                count_1++;
            }
    }
    ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
}
 
void USART1_IRQHandler(void)
{
    if(USART_GetFlagStatus(USART1,USART_FLAG_TXE))判断是否可以发送数据
    {
        USART1->CR1 &= ~USART_CR1_TXEIE;在这里笔者也碰到了问题,详见下文
        USART1->DR = car;
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
        //USART1->CR1 &= ~USART_CR1_TXEIE;
    }
    
    if(USART1->SR & USART_SR_RXNE)判断是否可以接收数据
    {
        volatile int8_t com_data;
        com_data = USART1->DR;
    }
}
在ADC中断服务函数中,代码中的if语句中的嵌套if语句是用来区分外接电源电压和内部参考电压的,因为第一次接受的是外部电源电压,第二次接受的是内部参考电压,所以可以利用奇偶数,即偶数用ADC_ConvertedValue来存放外部电源电压值,奇数时用ADC_ConvertedValue_1来存放参考电压值。但是,笔者发现,这样会失败,经过串口调试发现两次接受到的值都一样,笔者初步认为,可能是ADC的转换速率太快了,具体什么歌情况还不清楚,等下一个基础设计ADC转换(DMA方式)的时候在细究。

笔者,在调试程序的时候发现,通过中断的方式进行串口通信必须在给DR赋值之前将CR1寄存器的 TXEIE寄存器置0,以防止DE接受完数据后再次进入中断,要不然将数据赋值给DR后会一直进入中断(笔者在调试的时候发现,会一直重复输出一个值,笔者猜测这不是因为中断结束不了,而是因为,嵌套了N层循环,但是根据STM32参考手册的543页,TXEIE为明明解释是由软件设置,笔者在主函数里设置了TXEIE为0,发现没有用,如果在中断服务函数中不再次清零,就跳不出中断。),另外DR接受完数据后,还要加一句

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
要是没有这一句,串口调试时发现,一个值连续打印两次。(笔者猜测是不是因为电脑速度太快了。)

以上两个问题,笔者还没有找到原因,只是找到了解决方法,希望看到这篇文章的读者,如果知道原因,请不吝赐教,在下不胜感激。

---------------------------------------------------------------------------------------------------------------------------------

2018年3月27日补:

关于那个还要在中断服务函数中再次将TXEIE设置为零,是因为发送中断是通过 打开中断函数(USART_ITConfig)进入的,该函数中已经将CR1寄存器中的TXEIE位置1 的,所以中断服务函数中还要再次清0,以防止再中断。

---------------------------------------------------------------------------------------------------------------------------------

5,编写主函数
int main()
{
    void PrintU16(uint16_t num);
    void PrintHexU8(uint8_t data);
 
    Adc_Init();
    adc_nvic_init();
    usart_init();
    while(1)
    {
        num = (uint16_t)(2.0f* ADC_ConvertedValue/ADC_ConvertedValue_1 *1.2f*100 );参考电压是1.2f通过比例关系算出实际电压。
        PrintU16(num);
        delay(1000);
    }
}
笔者并没有计算出准确的电压值,只是采集到两个电压值,笔者猜测,可能是因为ADC的转换速率太快,导致笔者的  通过奇偶数来区分内部电压和外部电压不管用了,笔者还在思考解决方式,如果读者有好的方法,请指教。

本文到此结束,下面是完整代码:

#include<stm32f10x.h>
#define uint unsigned int
#define uchar unsigned char
    
char car;
static char count_1=0;//当做奇偶数
uint16_t ADC_ConvertedValue;//存放电源电压
uint16_t ADC_ConvertedValue_1;//存放内部参照电压
    static uint16_t num=0;
void delay(uint n)
{
    int i,j;
    for(i=0;i<n;i++)
    for(j=0;j<8500;j++);
}
 
void Adc_Init()
{
    ADC_InitTypeDef adc;
    GPIO_InitTypeDef io_b;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOB,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
    io_b.GPIO_Pin = GPIO_Pin_1;
    io_b.GPIO_Mode = GPIO_Mode_AIN;
    io_b.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&io_b);
    
    ADC_DeInit(ADC1);
    ADC_TempSensorVrefintCmd(ENABLE);
    adc.ADC_Mode = ADC_Mode_Independent;
    adc.ADC_ScanConvMode = ENABLE;
    adc.ADC_ContinuousConvMode = ENABLE;
    adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    adc.ADC_DataAlign = ADC_DataAlign_Right;
    adc.ADC_NbrOfChannel = 2;
    ADC_Init(ADC1,&adc);
    
    ADC_RegularChannelConfig(ADC1,ADC_Channel_9,1,ADC_SampleTime_239Cycles5);
    ADC_RegularChannelConfig(ADC1,ADC_Channel_17,2,ADC_SampleTime_239Cycles5);
    
    ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);
    ADC_Cmd(ADC1,ENABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);
}
 
void adc_nvic_init()
{
    
    NVIC_InitTypeDef nvic;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    nvic.NVIC_IRQChannel = ADC1_2_IRQn;
    nvic.NVIC_IRQChannelCmd = ENABLE;
    nvic.NVIC_IRQChannelPreemptionPriority = 1;
    nvic.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&nvic);
    
    NVIC_InitTypeDef usart1;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    usart1.NVIC_IRQChannel = USART1_IRQn;
    usart1.NVIC_IRQChannelCmd = ENABLE;
    usart1.NVIC_IRQChannelPreemptionPriority = 0;
    usart1.NVIC_IRQChannelSubPriority= 0;
    NVIC_Init(&usart1);
}
 
void usart_init()
{
     GPIO_InitTypeDef Uart_A;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);  
    Uart_A.GPIO_Pin = GPIO_Pin_9;  
    Uart_A.GPIO_Speed = GPIO_Speed_50MHz;  
    Uart_A.GPIO_Mode = GPIO_Mode_AF_PP;  
    GPIO_Init(GPIOA,&Uart_A);  
      
    Uart_A.GPIO_Pin = GPIO_Pin_10;  
    Uart_A.GPIO_Speed = GPIO_Speed_50MHz;  
    Uart_A.GPIO_Mode = GPIO_Mode_IN_FLOATING; 
    GPIO_Init(GPIOA,&Uart_A);   
    
    USART_InitTypeDef Uart;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);  
    Uart.USART_BaudRate = 115200;  
    Uart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
    Uart.USART_Mode = USART_Mode_Tx;  
    Uart.USART_Parity = USART_Parity_No;  
    Uart.USART_StopBits = USART_StopBits_1;  
    Uart.USART_WordLength = USART_WordLength_8b;  
    USART_Init(USART1,&Uart);  
      
    USART_Cmd(USART1,ENABLE);  
    USART_ClearFlag(USART1,USART_FLAG_TC); 
}
int main()
{
    void PrintU16(uint16_t num);
    void PrintHexU8(uint8_t data);
 
    Adc_Init();
    adc_nvic_init();
    usart_init();
    while(1)
    {
        num = (uint16_t)(2.0f* ADC_ConvertedValue/ADC_ConvertedValue_1 *1.2f*100 );
 
        PrintU16(num);
        delay(1000);
    }
}
 
void PrintU16(uint16_t num)
{
    void PrintHexU8(uint8_t data);
    uint8_t w5,w4,w3,w2,w1;
    w5 = num % 100000/10000;
    w4 = num % 10000/1000;
    w3 = num % 1000/100;
    w2 = num % 100/10;
    w1 = num % 10;
    PrintHexU8('0' + w5);
    PrintHexU8('0' + w4);
    PrintHexU8('0' + w3);
    PrintHexU8('0' + w2);
    PrintHexU8('0' + w1);
}
 
void PrintHexU8(uint8_t data)
{
    car = data;
    if(!(USART1->CR1 & USART_CR1_TXEIE))
        USART_ITConfig(USART1,USART_IT_TXE,ENABLE);//打开发送中断
}
 
void ADC1_2_IRQHandler()
{
    if(ADC_GetITStatus(ADC1,ADC_IT_EOC) == SET)
    {if(count_1%2 == 0){
        ADC_ConvertedValue = ADC_GetConversionValue(ADC1);
        count_1++;
    }else
            {
                ADC_ConvertedValue_1 = ADC_GetConversionValue(ADC1);
                count_1++;
            }
    }
    ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
}
 
void USART1_IRQHandler(void)
{
    if(USART_GetFlagStatus(USART1,USART_FLAG_TXE))
    {
        USART1->CR1 &= ~USART_CR1_TXEIE;
        USART1->DR = car;
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
        //USART1->CR1 &= ~USART_CR1_TXEIE;
    }
    
    if(USART1->SR & USART_SR_RXNE)
    {
        volatile int8_t com_data;
        com_data = USART1->DR;
    }
}


————————————————
版权声明:本文为CSDN博主「家安」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aa867734398/article/details/79710152

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值