找回密码
立即注册
搜索
热搜: 活动 交友 discuz
发新帖

343

积分

0

好友

31

主题
发表于 2023-9-9 20:08:35 | 查看: 275| 回复: 6 IP:中国 中国广播电视网络有限公司
CH32V307用rt-thread,ADC+DMA采样,采集到的数据只有8位,怎么回事


代码:
  1. uint16_t AD_Value[5];           //ADC采样数据

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

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

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

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

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

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

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

  42.     ADC_ResetCalibration(ADC1);                         //复位校准ADC1
  43.     while (ADC_GetResetCalibrationStatus(ADC1) == SET)
  44.         ; //等待复位校准完成
  45.     ADC_StartCalibration(ADC1);                         //开始校准ADC1
  46.     while (ADC_GetCalibrationStatus(ADC1) == SET)
  47.         ;      //等待校准完成

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

  51.         rt_thread_mdelay(100);
  52.     }
  53. }

  54. /* 线程 2 的入口函数,OLED屏显示信息 */
  55. static void thread2_OLED_entry(void *parameter)
  56. {
  57.     while (1)
  58.     {
  59.         char String[16];
  60.         u8g2_ClearBuffer(&u8g2);
  61.         u8g2_SetFont(&u8g2, u8g2_font_wqy15_t_chinese3); // 设置中文字符集
  62.         sprintf(String, "AD0:%d", AD_Value[0]); // 格式化字符串输出到字符串变量
  63.         u8g2_DrawStr(&u8g2, 0, 15, String);
  64.         sprintf(String, "AD1:%d", AD_Value[1]); // 格式化字符串输出到字符串变量
  65.         u8g2_DrawStr(&u8g2, 0, 31, String);
  66.         sprintf(String, "AD2:%d", AD_Value[2]); // 格式化字符串输出到字符串变量
  67.         u8g2_DrawStr(&u8g2, 0, 47, String);
  68.         sprintf(String, "AD3:%d", AD_Value[3]); // 格式化字符串输出到字符串变量
  69.         u8g2_DrawStr(&u8g2, 0, 63, String);
  70.         u8g2_SendBuffer(&u8g2); // 发送缓冲区数据
  71.         rt_thread_mdelay(100);  // 延时100毫秒
  72.     }
  73. }
复制代码
用rt-thread的Fin SH命令读取的数据是正常的
1689188661152084.png

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

1689188661927673.jpg














发表于 2023-9-9 20:09:21 IP:中国 中国广播电视网络有限公司
你仿真一下试试,可能是你显示的问题,你把(AD_Value&0xf00)>>8,把这个数格式化输出一下看看是不是0.
zeruns 发表于 2023-9-9 20:09 详情  回复
直接串口打印输出也是8位的数据,应该不是显示问题 按(AD_Value&0xf00)>>8这个格式输出,确实是输出0了,证明确实只读取到8位数据
发表于 2023-9-9 20:09:53 IP:中国 中国广播电视网络有限公司
酱油-哥 发表于 2023-9-9 20:09
你仿真一下试试,可能是你显示的问题,你把(AD_Value&0xf00)>>8,把这个数格式化输出一下看看是不是0. ...

直接串口打印输出也是8位的数据,应该不是显示问题
按(AD_Value&0xf00)>>8这个格式输出,确实是输出0了,证明确实只读取到8位数据
发表于 2023-9-9 20:10:34 IP:中国 中国广播电视网络有限公司
AD接口接3.3V时显示255,接GND时显示5左右
发表于 2023-9-9 20:11:02 IP:中国 中国广播电视网络有限公司
好像是DMA搬运的问题,我将ADC数据对齐设为左对齐,应该读取到数据为0xFFF0,但DMA搬运后读取到的是0xF0,只搬了8位数据
发表于 2023-9-9 20:12:04 IP:中国 中国广播电视网络有限公司
将DMA部分的代码复制官方的就可以了,但是我没发现跟我原先的代码有区别


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

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


酱油-哥 发表于 2023-9-9 20:13 详情  回复
明显是DMA外设宽度与地址宽度不一致, 用错宏了你
发表于 2023-9-9 20:13:23 IP:中国 中国广播电视网络有限公司
zeruns 发表于 2023-9-9 20:12
将DMA部分的代码复制官方的就可以了,但是我没发现跟我原先的代码有区别

明显是DMA外设宽度与地址宽度不一致,
  1. DMA_PeripheralDataSize_HalfWord
复制代码
  1. DMA_MemoryDataSize_HalfWord;
复制代码


用错宏了你

*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|国产电子社区 ( 沪ICP备2023018578号-1|

苏公网安备 32011102010465号


)|网站地图

GMT+8, 2024-6-25 06:31 , Processed in 0.094031 second(s), 29 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表