打印

为何使用STM32F103ZET6做数据采样,DMA与ADC传输错位

[复制链接]
7769|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
MariaBrook|  楼主 | 2013-1-22 12:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 MariaBrook 于 2013-1-22 18:09 编辑

    欧耶!终于碰上了传说中的“STM32F10x DMA+ADC 传输错位”问题。我使用DMA1_Channel1传输ADC1的11个通道数据,使用DMA2_Channel5传输ADC3的5个通道数据。ADC1和ADC3都由串口指令启动,启动后收集一组数据,发送给串口。
    关键的代码只有两个函数。时钟及其他功能已在主函数中实现。

#define ADC_AM1       11    // 用作ADC1
#define ADC_AM3        5    // 用作ADC3
#define ADC_AM     ADC_AM1 + ADC_AM3


volatile uint16_t adcResult_1[ADC_AM1];    // 存放ADC1的数据,DMA1_Channel1的目标
volatile uint16_t adcResult_3[ADC_AM3];    // 存放ADC3的数据,DMA2_Channel5的目标
volatile uint16_t adcResult[ADC_AM];         // 存放上面两个数组的数据

// **** 首先是初始化函数。这个函数里面的东东,大家应该比较熟悉了。基本功能就是初始化ADC1、ADC3、DMA1、DMA2 ********************
// ***************************************************************************************************************************
void ADC_initMyScan(void){
    uint8_t i;

    // **** 初始化IO口 ****
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    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(GPIOC, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //**** 初始化ADC1 ****
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;       // 不使用双ADC
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;           // 扫描模式打开
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;          // 不使用连续转换模式,而是把控制权交给串口
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    // 关闭外部触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;      // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = ADC_AM1;        // 规则通道11个
    ADC_Init(ADC1, &ADC_InitStructure);          // 构建ADC1
           
    for(i = 0; i < ADC_AM1; i++){
      ADC_RegularChannelConfig(adcUserADC_1, adcUserChannal_1, (i + 1), ADC_SampleTime_55Cycles5);  
    }
    ADC_Cmd(ADC1, ENABLE);
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1) == SET);

    // **** 初始化ADC3 ****
    ADC_DeInit(ADC3);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;       // 不使用双ADC
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;           // 扫描模式打开
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;          // 不使用连续转换模式,而是把控制权交给串口
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    // 关闭外部触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;      // 数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = ADC_AM3;        // 规则通道11个
    ADC_Init(ADC3, &ADC_InitStructure);          // 构建ADC1
           
    for(i = 0; i < ADC_AM3; i++){
      ADC_RegularChannelConfig(adcUserADC_3, adcUserChannal_3, (i + 1), ADC_SampleTime_55Cycles5);  
    }
    ADC_Cmd(ADC3, ENABLE);
    ADC_ResetCalibration(ADC3);
    while(ADC_GetResetCalibrationStatus(ADC3) == SET);
    ADC_StartCalibration(ADC3);
    while(ADC_GetCalibrationStatus(ADC3) == SET);

    // **** 配置DMA ****
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA1_C1_IRQ_LEVEL_PRE;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = DMA1_C1_IRQ_LEVEL_SUB;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Channel4_5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = DMA2_C5_IRQ_LEVEL_PRE;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = DMA2_C5_IRQ_LEVEL_SUB;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    DMA_DeInit(DMA1_Channel1);   
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR);       //DMA外设ADC基地址  
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&adcResult_1;       //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                    //从外设读取数据 DMA_DIR_PeripheralDST是从存储器读
    DMA_InitStructure.DMA_BufferSize = ADC_AM1;          //DMA通道的DMA缓存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;         //外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;       //内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   //数据宽度为16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;         //工作在循环缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;       //DMA通道x拥有高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;          //DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);           //根据DMA_InitStruct中指定的参数初始化DMA的通道
    DMA_Cmd(DMA1_Channel1, ENABLE);                  //启动DMA通道
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);        //构建DMA中断
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TE, ENABLE);        //构建DMA中断

    DMA_DeInit(DMA2_Channel5);   // 为ADC3
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC3->DR);       //DMA外设ADC基地址  
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&adcResult_3;       //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                    //从外设读取数据 DMA_DIR_PeripheralDST是从存储器读
    DMA_InitStructure.DMA_BufferSize = ADC_AM3;          //DMA通道的DMA缓存的大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;         //外设地址寄存器不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;       //内存地址寄存器递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   //数据宽度为16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;         //工作在循环缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;       //DMA通道x拥有高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;          //DMA通道x没有设置为内存到内存传输
    DMA_Init(DMA2_Channel5, &DMA_InitStructure);           //根据DMA_InitStruct中指定的参数初始化DMA的通道
    DMA_Cmd(DMA2_Channel5, ENABLE);                  //启动DMA通道
    DMA_ITConfig(DMA2_Channel5, DMA_IT_TC, ENABLE);        //构建DMA中断
    DMA_ITConfig(DMA2_Channel5, DMA_IT_TE, ENABLE);        //构建DMA中断

    ADC_DMACmd(ADC1, ENABLE);
    ADC_DMACmd(ADC3, ENABLE);
}

// **** 这个函数用来输出采样数据值 ********************************************************************
// **** adcDmaFinFlag_1和adcDmaFinFlag_3在DMA的传输完成中断中被置位 ************************************************
// ***********************************************************************************************************
uint8_t adc_debug(void){
    uint8_t i, flag;

    my_printf_SendString("\r\nEnter the ADC debug program!");
    my_printf_SendString("\r\nPlease enter s to start!");
    my_printf_SendString("\r\nPlease enter q to jump out!");

    while(1){
         flag = get_one_byte(&uart1DataTemp);
         switch (flag){
             case 's':
                    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
                    ADC_SoftwareStartConvCmd(ADC3, ENABLE);

                    while(adcDmaFinFlag_1 == FALSE);
                    adcDmaFinFlag_1 = FALSE;
                    while(adcDmaFinFlag_3 == FALSE);
                    adcDmaFinFlag_3 = FALSE;

                    ADC_resultToFinal();                 // **** 将数据赋给一个与外部借口对应的数组 *********************

                    my_printf_SendString("\r\nRound: ");
                    for(i = 0; i < ADC_AM; i++){
                         my_printf_SendASCII_num16Hex(adcResult);
                         my_printf_SendString(" ");
                    }
   
                    break;
            case 'q':
                    my_printf_SendString("\r\nEnd circle!");
                     return TRUE;
            default:
                     my_printf_SendString("\r\nCom is not valid! ");
                     break;
              }
       }                           
       return TRUE;
}

// **** 将数据赋给一个与外部借口对应的数组 *********************
// ******************************************************************
void ADC_resultToFinal(void){
    adcResult[0] = adcResult_3[0];
    adcResult[1] = adcResult_3[1];
    adcResult[2] = adcResult_3[2];
    adcResult[3] = adcResult_3[3];
    adcResult[4] = adcResult_3[4];
    adcResult[5] = adcResult_1[0];
    adcResult[6] = adcResult_1[1];
    adcResult[7] = adcResult_1[2];

    adcResult[8] = adcResult_1[3];
    adcResult[9] = adcResult_1[4];
    adcResult[10] = adcResult_1[5];
    adcResult[11] = adcResult_1[6];
    adcResult[12] = adcResult_1[7];
    adcResult[13] = adcResult_1[8];
    adcResult[14] = adcResult_1[9];
    adcResult[15] = adcResult_1[10];
}

DMA1_Channel1会将ADC1的11个数据存放在adcResult_1中
DMA2_Channel5会将ADC3的5个数据存放在adcResult_3中
串口会先显示adcResult_3的5个数,再显示adcResult_1的11个数,以“Round:”开始。

// **** 下面就是运行结果了 ***************************************************************************
// ******* 没有信号输入时 ****************
Round: 07E9 000D 0007 0008 0008 000C 0009 0008 0007 0007 0007 0008 0008 0007 0008 0008

// **** 通道有信号时 ADC3的5个通道分别加上信号*******************
Round: 07EE 01FF 0006 0004 0004 000D 0007 0008 0007 0008 0007 0007 0008 0007 0007 0007   
Round: 07E9 000D 0216 0009 0007 000D 0007 0008 0007 0007 0008 0007 0007 0007 0007 0007
Round: 07E1 0009 0005 01FF 0005 000D 0007 0008 0007 0008 0008 0007 0008 0007 0007 0007
Round: 085E 0008 0004 0004 0212 000E 0007 0007 0007 0007 0007 0008 0008 0007 0008 0007
Round: 07E9 000D 0007 0008 0008 000C 0009 0008 0007 0007 0007 0008 0008 0007 0008 0008

每个round里面的第一个数据,不知道是从哪里冒出来的。在使用示波器测量所有引脚为0V的情况下,这个数据依然存在。
ADC3本来应该在0、1、2、3的数据,到了1、2、3、4处,第五个通道上的数据被丢掉了。


// **** 通道有信号时 ADC1的11个通道分别加上信号*******************
Round: 07E1 000A 0005 0004 0005 000E 01FF 000A 0007 0007 0007 0007 0007 0007 0007 0007
Round: 07E2 0009 0005 0004 0004 021D 0009 0007 0007 0008 0007 0008 0007 0007 0007 0007
Round: 07E0 0009 0005 0004 0004 000D 0007 0007 0007 0007 0007 0212 0009 0007 0007 0007
Round: 07E0 0009 0005 0004 0004 000E 0007 0216 0009 0007 0007 0008 0008 0008 0008 0007
Round: 07DE 0009 0003 0006 0005 000E 0007 0007 09F1 000E 0007 0007 0007 0007 0008 0007
Round: 07E6 000D 0007 0007 0007 000E 0007 0009 0007 09EA 000E 0008 0007 0008 0007 0007
Round: 07E4 0009 0004 0004 0004 000D 0007 0007 0007 0008 0007 0008 0008 09E8 000E 0007
Round: 07E2 0009 0004 0005 0004 0011 0007 0008 0007 0008 0007 0008 0009 0008 0007 09EB
Round: 07E2 0009 0005 0005 0004 000D 0007 0007 0007 0008 0008 0008 0008 0007 09E8 000E
Round: 07E3 0009 0004 0004 0004 000D 0007 0007 0007 0008 09E8 000E 0008 0007 0007 0007
Round: 07DF 0009 0005 0004 0004 000E 0007 0008 0007 0008 0008 0007 09DD 000E 0007 0007

这些信号转换成电压值,都和输入信号一一对应。
那为什么ADC3的DMA传输出了错位的问题,而ADC1的DMA传输没有出问题呢?

沙发
goldsun_| | 2013-1-22 12:59 | 只看该作者
DMA_InitStructure.DMA_BufferSize = ADC_AM1;          //DMA通道的DMA缓存的大小
错了!!!!!

使用特权

评论回复
评分
参与人数 1威望 +2 收起 理由
MariaBrook + 2 很给力!
板凳
MariaBrook|  楼主 | 2013-1-22 13:11 | 只看该作者
goldsun_ 发表于 2013-1-22 12:59
DMA_InitStructure.DMA_BufferSize = ADC_AM1;          //DMA通道的DMA缓存的大小
错了!!!!! ...

为什么?难道不是11个数对应11个缓存吗?

使用特权

评论回复
地板
MariaBrook|  楼主 | 2013-1-22 14:11 | 只看该作者
如果初始化之后,只启动ADC1,或者只启动ADC3,现象和两个都启动是一样的
ADC3的数据仍然会丢掉最后一个,第一个数仍然不知道从哪里来的

使用特权

评论回复
5
MariaBrook|  楼主 | 2013-1-22 14:38 | 只看该作者
本帖最后由 MariaBrook 于 2013-1-22 14:40 编辑

for(i = 0; i < 20; i++){
    my_printf_SendASCII_num16Hex(*(adcResult_3 + i - 5));
    my_printf_SendString(" ");
}
如果使用指针观察adcResult_3的值,发现结果是
0008 0007 0007 0007 0007 0722 000D 0007 0007 0007 0722 000D 0007 0007 0007 000E 0008 0009 0007 0007
橙色是数组adcResult_1的值,紫色是数组adcResult的值。由于adcResult是由adcResult_3和adcResult_1组合起来赋值的,可以观察到adcResult的前五个数和adcResult_3是一样的。这就说明,DMA赋值的过程或者是之前的过程,就已经出错了。

使用特权

评论回复
6
MariaBrook|  楼主 | 2013-1-22 15:06 | 只看该作者
ADC不适用DMA,而是使用单次采样,发现采样的结果和使用DMA是一样的,同样丢失了第五位,还有一个莫名其妙的第一位。
这就说明不是程序的问题了,因为单次采样的程序运行成功过。
再检查电路图,发现把PF6~10接在了PF7~11~what a fucking day!

使用特权

评论回复
7
MariaBrook|  楼主 | 2013-1-22 18:10 | 只看该作者
结论:使用ADC+DMA,在单次扫描模式下,不会出现数据错位的问题。这种模式下,ADC+DMA还是非常稳定可靠的

使用特权

评论回复
8
yzzly| | 2013-1-23 15:31 | 只看该作者
本帖最后由 yzzly 于 2013-1-23 15:33 编辑

也许是你的通道配置的问题。

使用特权

评论回复
9
logokfu| | 2013-1-23 20:38 | 只看该作者
what a fucking day!:lol,还是自己的问题:lol

使用特权

评论回复
10
hxb20122012| | 2013-6-3 18:04 | 只看该作者
你好!我现在也用这个问题,使用两个DMA,采集ADC1的16个通道,ADC3的5个通道,发现数据错位的为题,请问,你的问题解决没?请教!!!!!

使用特权

评论回复
11
cjhk| | 2013-6-3 18:42 | 只看该作者
这一块   不是很了解   楼主   帮你顶一个   再看看别人的意见   希望你的问题能早点解决

使用特权

评论回复
12
hxb20122012| | 2013-6-3 20:05 | 只看该作者
我使用是的STM32F103ZET6,同时使用ADC1的16个通道,ADC3的5个通道,都使用DMA来传输,问题:比如ADC1_CH0通道上的值保存到了Buffer[1],其他数据往后移,ADC1_CH15的数据出现在Buffer[0]上,更不可失意的的是,有是ADC1_CH0通道的值出现在Buffer[2]上,其他的往后移,ADC1_CH14,ADC1_CH15则出现在Buffer[0],Buffer[1]上。请大家是否有朋友也碰到过。

使用特权

评论回复
13
dapan100| | 2017-9-8 14:45 | 只看该作者
程序问题

使用特权

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

本版积分规则

个人签名:天道酬勤

4

主题

29

帖子

0

粉丝