找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 315|回复: 22
打印 上一主题 下一主题
收起左侧

STC15单片机多通道ADC采集疑问

[复制链接]
跳转到指定楼层
楼主
30黑币
各位大神,如下代码可实现ADC单通道采集,已验证OK,想要实现双通道或者多通道采集应该如何修改代码?

如果需要在中断中修改代码,应该如何做到切换,以双通道为例子,感谢大家,谢谢!


#include "STC15.h"
#include <stdio.h>
#include "intrins.h"

#define ADC_POWER 0x80 // ADC电源控制位
#define ADC_FLAG 0x10  // ADC完成标志
#define ADC_START 0x08 // ADC起始控制位

#define ADC_SPEEDLL 0x00 // 540个时钟
#define ADC_SPEEDL 0x20  // 360个时钟
#define ADC_SPEEDH 0x40  // 180个时钟
#define ADC_SPEEDHH 0x60 // 90个时钟
#define VCC 5000         // set ADC_REF_VCC 设置ADC基准电压为5V

typedef unsigned char BYTE;
typedef unsigned int WORD;

BYTE ch = 0;   // ADC通道号
BYTE flag = 0; // 设置转换完成标志位
BYTE H8bit = 0, L2bit = 0;
WORD AD10bit = 0, temp = 0, dat = 0, dat1 = 0;
unsigned long result = 0;

void InitADC();
void UartInit(void);
char putchar(char c);
void Delay(WORD n);
void DisplayData(unsigned int iData);

void main()
{
    InitADC(); //初始化ADC
    while (1)
    {
        if (flag)
        {
            flag = 0;
            AD10bit = (AD10bit | H8bit) & 0x00ff;
            AD10bit = (AD10bit << 2) | (L2bit & 0X03); //合并成10位二进制数
            temp = AD10bit;
            AD10bit = 0;
            ADC_RES = 0;
            ADC_RESL = 0;
            printf("value_X=%d", temp);
            printf("              ");
            //注意:先把temp强制转换成long型,再进行电压转换才可获得正确的电压值
            result = ((long)temp * VCC / 1024);
            DisplayData(result);                                            printf("\r\n");
        }
    }
}

/*----------------------------
初始化ADC
----------------------------*/
void InitADC()
{
    P1ASF = 0x03;   //设置P1.0/P1.1口为AD口
    P1M1 = 0x03;    // set 1# chanel to AD        高阻
    P1M0 = 0x00;    // set 1# chanel to AD        高阻
    CLK_DIV = 0x00; //结果右对齐
    ADC_RES = 0;    //清除结果寄存器
    ADC_RESL = 0;
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START|ch ;
    Delay(10); // ADC上电并延时

    AUXR |= 0x10; // set EADCI
    IE = 0xa0;    // Enable ADC interrupt and Open master interrupt switch
}

/*----------------------------
软件延时
----------------------------*/
void Delay(WORD n)
{
    WORD x;

    while (n--)
    {
        x = 5000;
        while (x--);
    }
}

void DisplayData(unsigned int iData)
{
    char NumStr[4] = {0, 0, 0, 0}, i = 0;
    NumStr[0] = iData / 1000;         // 千位
    NumStr[1] = (iData % 1000) / 100; // 百位
    NumStr[2] = iData % 100 / 10;     // 十位
    NumStr[3] = iData % 10;           // 个位
    printf("ADC1=");
    printf("%c", NumStr[0] + 0x30);
    printf("%c", NumStr[1] + 0x30);
    printf(".");
    printf("%c", NumStr[2] + 0x30);
    printf("%c", NumStr[3] + 0x30);
}

/*----------------------------
ADC中断服务程序
----------------------------*/
void adc_isr() interrupt 5
{
    H8bit = ADC_RES;
    L2bit = ADC_RESL;
    ADC_CONTR &= !ADC_FLAG; //清除ADC中断标志
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
    flag = 1;
}

void UartInit(void)        //9600bps@11.0592MHz
{
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x01;                //串口1选择定时器2为波特率发生器
        AUXR &= 0xFB;                //定时器时钟12T模式
        T2L = 0xE8;                        //设置定时初始值
        T2H = 0xFF;                        //设置定时初始值
        AUXR |= 0x10;                //定时器2开始计时
        ES=1;                                                //允许串行口中断
        EA=1;                                                //允许总中断
}


char putchar(char c)
{
    UartInit();
    ES = 0;
    SBUF = c;
    while (TI == 0);
    TI = 0;
    ES = 1;
    return 0;
}



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:1071654 发表于 2024-4-7 17:35 | 只看该作者
先自顶下,有好的答案就采纳了
回复

使用道具 举报

板凳
ID:226055 发表于 2024-4-7 20:13 | 只看该作者
    1、你好,不知道你单片机的具体型号。我手头正好有艾克姆的STC15开发板,单片机是STC15W4K56S4,用这个硬件我调通了多路ADC。


    2、你的如果是STC15F应该也是可以的,具体你可以参考附件程序。


多路ADC.rar

246.18 KB, 下载次数: 3

回复

使用道具 举报

地板
ID:213173 发表于 2024-4-7 20:41 | 只看该作者
wen1989jun 发表于 2024-4-7 17:35
先自顶下,有好的答案就采纳了

STC有现成的示例

回复

使用道具 举报

5#
ID:1034262 发表于 2024-4-7 23:27 | 只看该作者
改通道数就可以了啊,ADC_CONTR低3位。可以直接参考STC官方的例子。
回复

使用道具 举报

6#
ID:1071654 发表于 2024-4-8 10:56 | 只看该作者
wulin 发表于 2024-4-7 20:41
STC有现成的示例

嗨,就是这个官方例程看得有点懵啊
回复

使用道具 举报

7#
ID:1071654 发表于 2024-4-8 10:58 | 只看该作者
coody_sz 发表于 2024-4-7 23:27
改通道数就可以了啊,ADC_CONTR低3位。可以直接参考STC官方的例子。

这个寄存器ADC_CONTR我只会修改其中一路,想要如:ADC0,ADC1通道分时用就不会了
回复

使用道具 举报

8#
ID:1071654 发表于 2024-4-8 10:58 | 只看该作者
cocolala 发表于 2024-4-7 20:13
1、你好,不知道你单片机的具体型号。我手头正好有艾克姆的STC15开发板,单片机是STC15W4K56S4,用这个 ...

感谢大神,我先用附件程序试试
回复

使用道具 举报

9#
ID:213173 发表于 2024-4-8 22:08 | 只看该作者
wen1989jun 发表于 2024-4-8 10:56
嗨,就是这个官方例程看得有点懵啊

你定义了变量  BYTE ch = 0;   // ADC通道号  ,在ADC转换程序中使用ch时没有改变ch值,那就只能重复采样0通道,要想多通道采集就得改变ch值,取值范围0-7。
回复

使用道具 举报

10#
ID:1109793 发表于 2024-4-9 09:36 | 只看该作者
搞个计数器,每次读完了就++嘛
回复

使用道具 举报

11#
ID:1071654 发表于 2024-4-9 14:42 | 只看该作者
wulin 发表于 2024-4-8 22:08
你定义了变量  BYTE ch = 0;   // ADC通道号  ,在ADC转换程序中使用ch时没有改变ch值,那就只能重复采样 ...

那这个要在程序上实现应该怎么写呢?
让ch在中断程序中自+,比如两通道>2就回到0?
不是太懂,望大神解惑,感谢!
回复

使用道具 举报

12#
ID:1071654 发表于 2024-4-9 14:43 | 只看该作者
xiaobendan001 发表于 2024-4-9 09:36
搞个计数器,每次读完了就++嘛

能稍微详细一点说说么,大哥
回复

使用道具 举报

13#
ID:1115937 发表于 2024-4-9 14:52 | 只看该作者
你查手册,把通道数对应就行了
回复

使用道具 举报

14#
ID:1071654 发表于 2024-4-9 15:03 | 只看该作者
目前在中断里面把ch++后,可以抓取P1.0,P1.1的ADC数据了,带来了新的问题应该怎么区分这两个数据呢?头大

void adc_isr() interrupt 5
{
    H8bit = ADC_RES;
    L2bit = ADC_RESL;
    ADC_CONTR &= !ADC_FLAG; //清除ADC中断标志
                ch++;
                ch%=2;
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
    flag = 1;
}
回复

使用道具 举报

15#
ID:192020 发表于 2024-4-9 15:20 | 只看该作者
wen1989jun 发表于 2024-4-9 15:03
目前在中断里面把ch++后,可以抓取P1.0,P1.1的ADC数据了,带来了新的问题应该怎么区分这两个数据呢?头大
...

你都知道改ch换通道了,怎么就不能判断ch是哪个通道呢
回复

使用道具 举报

16#
ID:1109793 发表于 2024-4-9 15:37 | 只看该作者
wen1989jun 发表于 2024-4-9 14:43
能稍微详细一点说说么,大哥

比如
弄个ad[8],i
i=0;
读出结果放ad[i ][ i];然后i++,启动第i通道
下次中断再读出还放ad[ i],然后再i++直到i>7则i=0;
回复

使用道具 举报

17#
ID:1115852 发表于 2024-4-9 16:32 | 只看该作者
ADC数模转化配合dmA数据搬运 可以配置一些单多通道,扫描模式来完成效果
回复

使用道具 举报

18#
ID:1071654 发表于 2024-4-9 18:35 | 只看该作者
qq475878026 发表于 2024-4-9 15:20
你都知道改ch换通道了,怎么就不能判断ch是哪个通道呢

那我好好想想,挺懵的
回复

使用道具 举报

19#
ID:1071654 发表于 2024-4-10 15:00 | 只看该作者
felicityocihjwe 发表于 2024-4-9 16:32
ADC数模转化配合dmA数据搬运 可以配置一些单多通道,扫描模式来完成效果

我现在是用中断做的获取ADC的值,扫描方式还不太会
回复

使用道具 举报

20#
ID:1071654 发表于 2024-4-10 15:00 | 只看该作者
xiaobendan001 发表于 2024-4-9 15:37
比如
弄个ad[8],i
i=0;

我试试,谢谢哥
回复

使用道具 举报

21#
ID:1071654 发表于 2024-4-10 15:03 | 只看该作者
目前按照下面代码方式能获取到两个通道的ADC值了,各位大哥帮忙看看有没有其他问题,感谢感谢!

#include "STC15.h"
#include <stdio.h>
#include "intrins.h"

#define ADC_POWER 0x80 // ADC电源控制位
#define ADC_FLAG 0x10  // ADC完成标志
#define ADC_START 0x08 // ADC起始控制位

#define ADC_SPEEDLL 0x00 // 540个时钟
#define ADC_SPEEDL 0x20  // 360个时钟
#define ADC_SPEEDH 0x40  // 180个时钟
#define ADC_SPEEDHH 0x60 // 90个时钟
#define VCC 5000         // set ADC_REF_VCC 设置ADC基准电压为5V

typedef unsigned char BYTE;
typedef unsigned int WORD;

BYTE ch ;   // ADC通道号
BYTE flag = 0; // 设置转换完成标志位
BYTE H8bit = 0, L2bit = 0;
WORD AD10bit = 0, temp = 0, dat = 0, dat1 = 0;
unsigned long result = 0;result1 = 0;

void InitADC();
void UartInit(void);
char putchar(char c);
void Delayms(unsigned xms);
void DisplayData(unsigned int iData);

void main()
{
    InitADC(); //初始化ADC
    while (1)
    {
        if (flag)
        {
                                        if(ch==1)
                                        {
            flag = 0;
            AD10bit = (AD10bit | H8bit) & 0x00ff;
            AD10bit = (AD10bit << 2) | (L2bit & 0X03); //合并成10位二进制数
            temp = AD10bit;
            AD10bit = 0;
            ADC_RES = 0;
            ADC_RESL = 0;
//            printf("value_X=%d", temp);
//            printf("              ");
            //注意:先把temp强制转换成long型,再进行电压转换才可获得正确的电压值
            result = ((long)temp * VCC / 1024);
//            DisplayData(result);
//            printf("\r\n");
                                               
                                        }
                                        if(ch==0)
                                        {
                                                flag = 0;
            AD10bit = (AD10bit | H8bit) & 0x00ff;
            AD10bit = (AD10bit << 2) | (L2bit & 0X03); //合并成10位二进制数
            dat = AD10bit;
            AD10bit = 0;
            ADC_RES = 0;
            ADC_RESL = 0;
//            printf("value_Y=%d", dat);
//            printf("              ");
            //注意:先把dat强制转换成long型,再进行电压转换才可获得正确的电压值
            result1 = ((long)dat * VCC / 1024);
//            DisplayData(result);
//            printf("\r\n");

                                        }
        }
    }
}

/*----------------------------
初始化ADC
----------------------------*/
void InitADC()
{
    P1ASF = 0x03;   //设置P1.0/P1.1口为AD口
    P1M1 = 0x03;    // set 1# chanel to AD        高阻
    P1M0 = 0x00;    // set 1# chanel to AD        高阻
    CLK_DIV = 0x00; //结果右对齐
    ADC_RES = 0;    //清除结果寄存器
    ADC_RESL = 0;
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START|ch ;
    Delayms(1); // ADC上电并延时1ms
    AUXR |= 0x10; // set EADCI
    IE = 0xa0;    // Enable ADC interrupt and Open master interrupt switch
}

/*----------------------------
软件延时
----------------------------*/

void Delayms(unsigned xms)        //@11.0592MHz
{
        unsigned char data i, j;

        while(xms--)
        {
        _nop_();
        _nop_();
        _nop_();
        i = 11;
        j = 190;
        do
        {
                while (--j);
        } while (--i);
               
        }
}


void DisplayData(unsigned int iData)
{
    char NumStr[4] = {0, 0, 0, 0}, i = 0;
    NumStr[0] = iData / 1000;         // 千位
    NumStr[1] = (iData % 1000) / 100; // 百位
    NumStr[2] = iData % 100 / 10;     // 十位
    NumStr[3] = iData % 10;           // 个位
    printf("ADC1=");
    printf("%c", NumStr[0] + 0x30);
    printf("%c", NumStr[1] + 0x30);
    printf(".");
    printf("%c", NumStr[2] + 0x30);
    printf("%c", NumStr[3] + 0x30);
}

/*----------------------------
ADC中断服务程序
----------------------------*/
void adc_isr() interrupt 5
{
    H8bit = ADC_RES;
    L2bit = ADC_RESL;
    ADC_CONTR &= !ADC_FLAG; //清除ADC中断标志
                ch++;
                ch%=2;
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
    flag = 1;
}

void UartInit(void)        //9600bps@11.0592MHz
{
        SCON = 0x50;                //8位数据,可变波特率
        AUXR |= 0x01;                //串口1选择定时器2为波特率发生器
        AUXR &= 0xFB;                //定时器时钟12T模式
        T2L = 0xE8;                        //设置定时初始值
        T2H = 0xFF;                        //设置定时初始值
        AUXR |= 0x10;                //定时器2开始计时
        ES=1;                                                //允许串行口中断
        EA=1;                                                //允许总中断
}


char putchar(char c)
{
    UartInit();
    ES = 0;
    SBUF = c;
    while (TI == 0);
    TI = 0;
    ES = 1;
    return 0;
}

回复

使用道具 举报

22#
ID:1092316 发表于 2024-4-10 21:22 来自手机 | 只看该作者
wen1989jun 发表于 2024-4-10 15:00
我试试,谢谢哥

这个可以用,你改一下。
#include "stc8g.h"     //读多通道ADC
#include "intrins.h"

typedef     unsigned char   u8;
typedef     unsigned int    u16;

#define ADCTIM (*(unsigned char volatile xdata *)0xfea8)

u16 Get(u8 channel); //channel = 0~15

void maindd(void)
  {          if(Get(3)>150)       
                {  P55=0;}
        else{P55=1;}}
         
void main(void)
{  P_SW2 |= 0x80;  //扩展寄存器(XFR)访问使能

    P3M0 &= ~0x0f; P3M1 |= 0x0f; //高阻
    P5M0 &= ~0x30; P5M1 &= ~0x30;

     ADCTIM = 0x3f;      //设置 ADC 内部时序,ADC采样时间建议设
    ADCCFG = 0x2f;      //设置 ADC 时钟为系统时钟/2/16
    ADC_CONTR = 0x80; //使能 ADC 模块
       
   while(1)
   {
        maindd();
                            }}

u16 Get(u8 channel)  //channel = 0~15
{
    ADC_RES = 0;
    ADC_RESL = 0;

    ADC_CONTR = (ADC_CONTR & 0xF0) | 0x40 | channel;    //启动 AD 转换
    _nop_();
    _nop_();
    _nop_();
    _nop_();

    while((ADC_CONTR & 0x20) == 0)  ;   //wait for ADC finish
    ADC_CONTR &= ~0x20;     //清除ADC结束标志
    return  (((u16)ADC_RES << 8) | ADC_RESL);
}
回复

使用道具 举报

23#
ID:1071654 发表于 2024-4-11 10:12 | 只看该作者
jch352122 发表于 2024-4-10 21:22
这个可以用,你改一下。
#include "stc8g.h"     //读多通道ADC
#include "intrins.h"

谢谢哥,我先去试试。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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