CH32V307用rt-thread,ADC+DMA采样,采集到的数据只有8位,怎么回事

CH32V307用rt-thread,ADC+DMA采样,采集到的数据只有8位,怎么回事

代码:

uint16_t AD_Value[5];           //ADC采样数据

/* 线程 3 的入口函数,ADC数据处理 */
static void thread3_ADC_entry(void *parameter)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //启用ADC1时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);   //启用GPIOA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      //启用DMA1时钟

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);       //ADC时钟分配配置,6分频(72Mhz/6=12Mhz),ADC时钟频率不能大于14Mhz

    GPIO_InitTypeDef GPIO_InitStructure;                //定义结构体配置GPIO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;       //设置GPIO口为AIN模拟输入模式
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;    //设置GPIO口
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   //设置GPIO口速度50Mhz
    GPIO_Init(GPIOA, &GPIO_InitStructure);              //初始化GPIOA

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //配置ADC规则组,在规则组的序列1写入通道0,采样时间55.5个周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //配置ADC规则组,在规则组的序列2写入通道1,采样时间55.5个周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); //配置ADC规则组,在规则组的序列3写入通道2,采样时间55.5个周期
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5); //配置ADC规则组,在规则组的序列4写入通道3,采样时间55.5个周期

    ADC_InitTypeDef ADC_InitStructure;                      //定义结构体配置ADC
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;      //设置ADC模式,独立模式
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //设置数据对齐模式,右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发源选择,不使用外部触发(使用软件触发)
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;      //启用连续转换模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;            //启用扫描模式
    ADC_InitStructure.ADC_NbrOfChannel = 4;                 //扫描模式下用到的通道数目
    ADC_Init(ADC1, &ADC_InitStructure);                     //初始化ADC1

    DMA_InitTypeDef DMA_InitStructure;                      //定义结构体配置DMA
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC1->RDATAR; //外设基地址(源数据地址),设置为ADC1_RDATAR寄存器
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord; //外设数据宽度,设置为半字,16位
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    //外设地址是否自增,不自增
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) AD_Value;          //内存基地址,数组变量为地址,需强制转换为uint32_t
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据宽度,设置为为半字,16位
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             //内存地址是否自增,设置为自增
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                  //指定外设地址为源端,传输方向为外设到内存
    DMA_InitStructure.DMA_BufferSize = 4;                               //传输计数器,设置传输4次
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                     //设置DMA模式为循环模式(自动重装计数器)与软件触发不能同时使用。
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                        //设置不使用软件触发,即硬件触发
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;               //设置通道的转运优先级为中等
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);                        //初始化DMA1的通道1,ADC1的硬件触发接在DMA1的通道1上,所以必须使用DMA1通道1

    DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA1通道1
    ADC_DMACmd(ADC1, ENABLE);       //开启ADC1的DMA触发信号
    ADC_Cmd(ADC1, ENABLE);          //开启ADC1

    ADC_ResetCalibration(ADC1);                         //复位校准ADC1
    while (ADC_GetResetCalibrationStatus(ADC1) == SET)
        ; //等待复位校准完成
    ADC_StartCalibration(ADC1);                         //开始校准ADC1
    while (ADC_GetCalibrationStatus(ADC1) == SET)
        ;      //等待校准完成

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);             //软件触发ADC转换
    while (1)
    {

        rt_thread_mdelay(100);
    }
}

/* 线程 2 的入口函数,OLED屏显示信息 */
static void thread2_OLED_entry(void *parameter)
{
    while (1)
    {
        char String[16];
        u8g2_ClearBuffer(&u8g2);
        u8g2_SetFont(&u8g2, u8g2_font_wqy15_t_chinese3); // 设置中文字符集
        sprintf(String, "AD0:%d", AD_Value[0]); // 格式化字符串输出到字符串变量
        u8g2_DrawStr(&u8g2, 0, 15, String);
        sprintf(String, "AD1:%d", AD_Value[1]); // 格式化字符串输出到字符串变量
        u8g2_DrawStr(&u8g2, 0, 31, String);
        sprintf(String, "AD2:%d", AD_Value[2]); // 格式化字符串输出到字符串变量
        u8g2_DrawStr(&u8g2, 0, 47, String);
        sprintf(String, "AD3:%d", AD_Value[3]); // 格式化字符串输出到字符串变量
        u8g2_DrawStr(&u8g2, 0, 63, String);
        u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
        rt_thread_mdelay(100);  // 延时100毫秒
    }
}


用rt-thread的Fin SH命令读取的数据是正常的

1689188661152084.png

用DMA读取到的数据只有8位

1689188661927673.jpg


你仿真一下试试,可能是你显示的问题,你把(AD_Value&0xf00)>>8,把这个数格式化输出一下看看是不是0.


直接串口打印输出也是8位的数据,应该不是显示问题


(AD_Value&0xf00)>>8这个格式输出,确实是输出0了,证明确实只读取到8位数据


AD接口接3.3V时显示255,接GND时显示5左右


好像是DMA搬运的问题,我将ADC数据对齐设为左对齐,应该读取到数据为0xFFF0,但DMA搬运后读取到的是0xF0,只搬了8位数据


将DMA部分的代码复制官方的就可以了,但是我没发现跟我原先的代码有区别

官方的:

    DMA_InitTypeDef DMA_InitStructure;                      //定义结构体配置DMA
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) &ADC1->RDATAR; //配置外设地址为ADC数据寄存器地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32) AD_Value;          //配置存储器地址为读取ADC值地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;              //配置数据源为外设,即DMA传输方式为外设到存储器
    DMA_InitStructure.DMA_BufferSize = 4;                           //设置DMA数据缓冲区大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA外设递增模式关闭
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;         //设置DMA存储器递增模式开启
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //设置外设数据大小为半字,即两个字节
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;         //设置存储器数据大小为半字,即两个字节
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;     //设置DMA模式为循环传输模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置DMA传输通道优先级为高,当使用一 DMA通道时,优先级设置不影响
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;        //因为此DMA传输方式为外设到存储器,因此禁用存储器到存储器传输方式
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);        //初始化DMA1的通道1,ADC1的硬件触发接在DMA1的通道1上,所以必须使用DMA1通道1


原先的:

DMA_InitTypeDef DMA_InitStructure;                      //定义结构体配置DMA
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) &ADC1->RDATAR; //外设基地址(源数据地址),设置为ADC1_RDATAR寄存器
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord; //外设数据宽度,设置为半字,16位
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    //外设地址是否自增,不自增
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32) AD_Value;          //内存基地址,数组变量为地址,需强制转换为uint32_t
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据宽度,设置为为半字,16位
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             //内存地址是否自增,设置为自增
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                  //指定外设地址为源端,传输方向为外设到内存
    DMA_InitStructure.DMA_BufferSize = 4;                               //传输计数器,设置传输4次
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                     //设置DMA模式为循环模式(自动重装计数器)与软件触发不能同时使用。
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                        //设置不使用软件触发,即硬件触发
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;               //设置通道的转运优先级为中等
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);                        //初始化DMA1的通道1,ADC1的硬件触发接在DMA1的通道1上,所以必须使用DMA1通道1



明显是DMA外设宽度与地址宽度不一致,

 DMA_PeripheralDataSize_HalfWord
DMA_MemoryDataSize_HalfWord;

用错宏了你


只有登录才能回复,可以选择微信账号登录