mwx17 发表于 2015-12-12 11:17:58

新人求助,adc采样频率问题

我是一名学中医的学生,因为学习需要想用个脉诊仪,因为各种你懂得而我也无奈的规定无法借用,只好尝试自己做一个。我买了一个stm32f103+ads1256的板子,接上一个薄膜压敏电阻,期望能够通过压敏电阻来采集手腕处血管压力值的变化输出的电压值,把采集到的电压值输出为波形图(折线图)来获得有意义的脉波图。结果遇到问题了。。。:

​这个板子有单路adc采集的程序提供,用串口转usb链接电脑,可以使用串口调试助手来接受板子发来的ASCII码数据,数据为小数点后5位的电压值,例:CH0:0.22422V

​现在的问题是:串口调试助手接收到的数据频率太低,精度也太低,无法在excel上转化为有意义的波形图(折线图)。
​请教:应该修改哪些语句来实现提高串口调试助手接收到的数据频率和提高电压值的精度呢?

​我翻了《Cortex-M3权威指南》《STM32F103XXX参考手册》《STM32固件库使用》《STM32F103中文教程及参考手册》。。。。等多本参考书,只知道需要设定时钟和adc,限于自身低劣的编程知识,无力找到对应的语句。可是这个东西的实现,对我的学习真的是非常重要,真心希望能够得到你们的帮助!谢谢!

​附件里面有全部的程序,包括单路采集程序和发指令采集程序(我一直没理解两者的区别。。。)



​下附“单路采集程序”内“USER”目录的:MAIN.C   如果需要其他文件代码,我会尽快贴上来。
​#include "stm32f10x.h"
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
uchar dat,a,ds,i;
long AD_DATA;

#define K1 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0)
/*************************** 宏定义***********************************************/
/*Registers' Address*/
#define REG_STATUS0   
#define REG_MUX   1
#define REG_ADCON   2
#define REG_DRATE   3
#define REG_IO      4
#define REG_OFC0    5
#define REG_OFC1    6
#define REG_OPC2    7
#define REG_FSC0    8
#define REG_FSC1    9
#define REG_FSC2    10

/*Operation Command*/
//#define CMD_WAKEUP   0x00
#define CMD_RDATA      0x01
#define CMD_RDATAC   0x03
#define CMD_SDATAC   0x0F
#define CMD_RREG       0x10
#define CMD_WREG       0x50
#define CMD_SELFCAL    0xf0
#define CMD_SELFOCAL   0xf1
#define CMD_SELFGCAL   0xf2
#define CMD_SYSOCAL    0xf3
#define CMD_SYSGCAL    0xf4
#define CMD_SYNC       0xfc
#define CMD_STANDBY    0xfd
#define CMD_RESET      0xfe
#define CMD_WAKEUP   0xFF

#define PGA_1            0x00
#define PGA_2            0x01
#define PGA_4            0x02
#define PGA_8            0x03
#define PGA_16         0x04
#define PGA_32         0x05
#define PGA_64         0x06

#define POSITIVE_AIN0            (0X00<<4)
#define POSITIVE_AIN1            (0X01<<4)
#define POSITIVE_AIN2            (0X02<<4)
#define POSITIVE_AIN3            (0X03<<4)
#define POSITIVE_AIN4            (0X04<<4)
#define POSITIVE_AIN5            (0X05<<4)
#define POSITIVE_AIN6            (0X06<<4)
#define POSITIVE_AIN7            (0X07<<4)
#define POSITIVE_AINCOM          (0X08<<4)      

#define NEGTIVE_AIN0            0X00
#define NEGTIVE_AIN1            0X01
#define NEGTIVE_AIN2            0X02
#define NEGTIVE_AIN3            0X03
#define NEGTIVE_AIN4            0X04
#define NEGTIVE_AIN5            0X05
#define NEGTIVE_AIN6            0X06
#define NEGTIVE_AIN7            0X07
#define NEGTIVE_AINCOM            0X08

/*For fclkin=7.68MHz, data rate*/
#define DATARATE_30K            0xf0
#define DATARATE_15K            0xe0
#define DATARATE_7_5K             0xd0
#define DATARATE_3_7_5K         0xc0
#define DATARATE_2K               0xb0

/*STATUS REGISTER*/
#define MSB_FRIST                (0x00<<3)
#define LSB_FRIST                (0x01<<3)
#define ACAL_OFF               (0x00<<2)
#define ACAL_ON                  (0x01<<2)
#define BUFEN_OFF                (0x00<<1)
#define BUFEN_ON               (0x01<<1)

/*ADCON REGISTER*/
#define CLKOUT_OFF               (0x00<<5)
#define CLKOUT_CLKIN             (0x01<<5)
#define DETECT_OFF               (0x00<<3)
#define DETECT_ON_2UA            (0x02<<3)

#define ADS1256_DRDY    ((GPIOB->IDR)&(1<<12))   //        GPIOE->IDR读端口E的数据寄存器
#define ADS1256_DO      ((GPIOB->IDR)&(1<<13))

#define SetADS1256_DRDY   GPIOB->BSRR =(1<<12)   //BSRR对低16位中的某位置'1'则它对应的端口位被置'1'
#define SetADS1256_DO   GPIOB->BSRR =(1<<13)

//#define ADS1256_SYNC   (1<<8)
#define ADS1256_CS   (1<<11)
#define ADS1256_IN   (1<<14)
#define ADS1256_CLK    (1<<15)

//#define SetADS1256_SYNC   GPIOB->BSRR =ADS1256_SYNC
#define SetADS1256_CS   GPIOB->BSRR =ADS1256_CS
#define SetADS1256_IN   GPIOB->BSRR =ADS1256_IN
#define SetADS1256_CLK    GPIOB->BSRR =ADS1256_CLK

#define ClrADS1256_SYNC   GPIOB->BRR =ADS1256_SYNC   //BRR对低16位中的某位置'0'则端口x的对应位被清'0'
#define ClrADS1256_CS   GPIOB->BRR =ADS1256_CS
#define ClrADS1256_IN   GPIOB->BRR =ADS1256_IN
#define ClrADS1256_CLK    GPIOB->BRR =ADS1256_CLK


unsigned char result;

#define ADCLK RCC_APB2Periph_GPIOA//定义AD芯片所使用的I/O端口的时钟。
/*Registers' Address*/
#define REG_STATUS0
#define REG_MUX   1
#define REG_ADCON   2
#define REG_DRATE   3
#define REG_IO      4
#define REG_OFC0    5
#define REG_OFC1    6
#define REG_OPC2    7
#define REG_FSC0    8
#define REG_FSC1    9
#define REG_FSC2    10

/*Operation Command*/
//#define CMD_WAKEUP   0x00
#define CMD_RDATA      0x01
#define CMD_RDATAC   0x03
#define CMD_SDATAC   0x0F
#define CMD_RREG       0x10
#define CMD_WREG       0x50
#define CMD_SELFCAL    0xf0
#define CMD_SELFOCAL   0xf1
#define CMD_SELFGCAL   0xf2
#define CMD_SYSOCAL    0xf3
#define CMD_SYSGCAL    0xf4
#define CMD_SYNC       0xfc
#define CMD_STANDBY    0xfd
#define CMD_RESET      0xfe
#define CMD_WAKEUP   0xFF

/*************************************************/
void delayad(unsigned int tt)
{
unsigned int i,j;
for(j=tt;j>0;j--)
for(i=5;i>0;i--);
}

/*************************************************/
void delayad_nopar(void)
{
unsigned long i =50;
           while(i--);
}


/*************************************************
函数: void RCC_Configuration(void)
功能: 复位和时钟控制 配置
参数: 无
返回: 无
**************************************************/

void RCC_Configuration(void)//配置时钟
{
ErrorStatus HSEStartUpStatus;                  //定义外部高速晶体启动状态枚举变量
RCC_DeInit();                                    //复位RCC外部设备寄存器到默认值
RCC_HSEConfig(RCC_HSE_ON);                     //打开外部高速晶振
HSEStartUpStatus = RCC_WaitForHSEStartUp();      //等待外部高速时钟准备好
if(HSEStartUpStatus == SUCCESS)                  //外部高速时钟已经准别好
{
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //开启FLASH预读缓冲功能,加速FLASH的读取。所有程序中必须的用法.位置:RCC初始化子函数里面,时钟起振之后
    FLASH_SetLatency(FLASH_Latency_2);                  //flash操作的延时

    RCC_HCLKConfig(RCC_SYSCLK_Div2);               //配置AHB(HCLK)时钟等于==SYSCLK
    RCC_PCLK1Config(RCC_HCLK_Div2);          //配置APB1(PCLK1)钟==AHB1/2时钟
RCC_PCLK2Config(RCC_HCLK_Div2);                //配置APB2(PCLK2)钟==AHB时钟


    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);//配置PLL时钟 == 外部高速晶体时钟 * 9 = 72MHz
    RCC_PLLCmd(ENABLE);                                 //使能PLL时钟

    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)    //等待PLL时钟就绪
    {
    }
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);            //配置系统时钟 = PLL时钟
    while(RCC_GetSYSCLKSource() != 0x08)                  //检查PLL时钟是否作为系统时钟
    {
    }
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO, ENABLE);
   //允许GPIOB、AFIO时钟
}

/*******************************************************************************
函数: GPIO_Configuration(void)
功能: 配置GPIO口
参数: 无
返回: 无
*******************************************************************************/
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;      //定义GPIO初始化结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_11|GPIO_Pin_14|GPIO_Pin_15 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_12|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}


/*******************************************************************************
函数:   NVIC_Configuration(void)
功能:   配置中断功能
输入:      无
输出:      无      
返回:      无      
*******************************************************************************/

void NVIC_Configuration(void)
{
   NVIC_InitTypeDef NVIC_InitStructure;
   NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

   /* Configure the NVIC Preemption Priority Bits */
   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

   /* Enable the USART1 Interrupt */
   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;       //通道设置为串口1中断
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;          //中断响应优先级0
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;          //打开中断
   NVIC_Init(&NVIC_InitStructure);   //初始化
}

/*******************************************************************************
函数名:USART1_Configuration
输 入:
输 出:
功能说明:配置串口参数
******************************************************************************/
void USART1_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;

/* 打开GPIO和USART部件的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

/* 将USART Tx的GPIO配置为推挽复用模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

/* 将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);


/*   配置USART1参数
   - BaudRate = 9600 baud
   - Word Length = 8 Bits
   - One Stop Bit
   - No parity
   - Hardware flow control disabled (RTS and CTS signals)
   - Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate =115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);

    /* 若接收数据寄存器满,则产生中断 */
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

/* 使能 USART1, 配置完毕 */
USART_Cmd(USART1, ENABLE);

    /* 如下语句解决第1个字节无法正确发送出去的问题 */
    USART_ClearFlag(USART1, USART_FLAG_TC);   // 清标志
}

/*******************************************************************************
函数名:Uart1_PutChar()
输入:
输出:
功能说明:串口发送一字节数据
********************************************************************************/

void Uart1_PutChar(u8 ch)
{
USART_SendData(USART1, (u8) ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/*******************************************************************************
函数: USART1_IRQHandler(void)
功能: 串口中断函数
参数: 无
返回: 无
*******************************************************************************/
void USART1_IRQHandler(void)            //在中断服务程序中,由于主机响应中断时并不知道是哪个中断源发出中断请求
{
          //是不用做上述判别的。但是无论什么情况,做上述判别是个好习惯
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)    //若接收数据寄存器满
{   
    dat = USART_ReceiveData(USART1);          
}
}

/*******************************************************************************
函数: ADS1256_Write_Byte(unsigned char d)
功能: 写入一字节数据
参数: 无
返回: 无
*******************************************************************************/
void ADS1256_Write_Byte(unsigned char d)
{
unsigned char i=8;
ClrADS1256_CLK ;
           while(i--)
{
if(d & 0X80)
        SetADS1256_IN;
else
ClrADS1256_IN;
      delayad_nopar();       
SetADS1256_CLK;
        delayad_nopar();
        ClrADS1256_CLK ;
        delayad_nopar();
        d <<= 1;
}
}

/*******************************************************************************
函数: ADS1256_Read_Byte(void)
功能: 读取一字节数据
参数: 无
返回: 无
*******************************************************************************/

unsigned char ADS1256_Read_Byte(void)
{
unsigned char i=8,d;   
ClrADS1256_CLK;       
           while(i--)
    {
      d <<=1;
        SetADS1256_CLK;
      delayad_nopar();
      ClrADS1256_CLK;   
delayad_nopar();
SetADS1256_DO;
        if(ADS1256_DO)
                   d |= 0x01;
        else
                   d &= 0xfe;             
}   
           return d;

}


/******************************************************************************
函数: ADS1256_Write_Reg(unsigned char reg_name, unsigned char reg_data)
功能: 写寄存器函数
参数: 无
返回: 无

*******************************************************************************/

voidADS1256_Write_Reg(unsigned char reg_name, unsigned char reg_data)
{
while(ADS1256_DRDY);
ADS1256_Write_Byte(CMD_WREG|reg_name);
           ADS1256_Write_Byte(0x00);
           ADS1256_Write_Byte(reg_data);
}


/*******************************************************************************
函数: ADS1256_Read_dat(void)
功能: 读取ADS1256三字节数据
参数: 无
返回: 无
*******************************************************************************/
voidADS1256_Read_dat(void)
{
ClrADS1256_CS;
while(ADS1256_DRDY==0);
while(ADS1256_DRDY);        //DRDY信号为低表示AD转换完成
           ADS1256_Write_Byte(CMD_RDATA);
delayad(100);   //min=50*(1/fCLKIN)=50*(1/7.68MHZ)=6500ns;max=whatever
           result = ADS1256_Read_Byte();
           result = ADS1256_Read_Byte();
           result = ADS1256_Read_Byte();
SetADS1256_CS;
}

/*******************************************************************************
函数: ADS1256_int()
功能: 初始化ADS1256
参数: 无
返回: 无
*******************************************************************************/
void ADS1256_int()
{
ClrADS1256_CLK;
delayad(2);
ClrADS1256_CS;
while(!ADS1256_DRDY);
while(ADS1256_DRDY);
ADS1256_Write_Byte(CMD_RESET);
while(ADS1256_DRDY);
ADS1256_Write_Reg(REG_STATUS,0xf4);//STATUS REGISTER:Auto-Calibration Enabled,Analog Input Buffer Disabled

while(ADS1256_DRDY);
ADS1256_Write_Reg(REG_MUX,POSITIVE_AIN0+ NEGTIVE_AINCOM);//MUX:AIN0
//while(ADS1256_DRDY);
    //ADS1256_Write_Reg(REG_MUX,0x08);//AIN0 is Positive,single-ended measurements
while(ADS1256_DRDY);
ADS1256_Write_Reg(REG_ADCON,CLKOUT_OFF+DETECT_OFF+PGA_1);          //ADCON=00h
ADS1256_Write_Reg(REG_DRATE,DATARATE_15K);//data rate 15k SPS
while(ADS1256_DRDY);
ADS1256_Write_Byte(CMD_WAKEUP);
delayad(10);
SetADS1256_CS;
}


/*******************************************************************************
函数:Write_Reg_Mux()
功能: 读取数据并通过UART1 发送数据
参数: 无
返回: 无
*******************************************************************************/

longWrite_Reg_Mux(uchar x)
{        uchar k;
   long int AD_temp;
      dat=0;
ClrADS1256_CS;
ADS1256_Write_Reg(REG_MUX,x);//选择通道

ADS1256_Read_dat();
if(result>0x7f)
result=result=        result=0;
AD_temp=((result*65536+result*256+result)*5)/83.88607;
returnAD_temp;
}

void Send_data_ascii(uchar y)
{
      ds=AD_DATA/100000;
ds=AD_DATA%100000/10000;
ds=AD_DATA%10000/1000;
ds=AD_DATA%1000/100;
ds=AD_DATA%100/10;
ds=AD_DATA%100%10;
Uart1_PutChar('C');
Uart1_PutChar('H');
Uart1_PutChar(y+0x30);
Uart1_PutChar(':');
Uart1_PutChar(ds+0X30);
Uart1_PutChar('.');
Uart1_PutChar(ds+0X30);       
Uart1_PutChar(ds+0X30);
Uart1_PutChar(ds+0X30);
Uart1_PutChar(ds+0X30);
Uart1_PutChar(ds+0X30);
Uart1_PutChar('V');


}

/*********************************************************************************
函数: int main(void)
功能: main主函数
参数: 无
返回: 无
*********************************************************************************/

int main(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
ADS1256_int();

USART1_Configuration();

while(1)
{

a=POSITIVE_AIN0+ NEGTIVE_AINCOM;
for(i=0;i<20;i++)
AD_DATA+=Write_Reg_Mux(a);//选择通道0
AD_DATA/=20;
Send_data_ascii(0);
AD_DATA=0;
Uart1_PutChar(' ');
                            
Uart1_PutChar(0x0d);
Uart1_PutChar(0x0a);
}
}


munsterzl 发表于 2015-12-12 13:04:15

不懂帮顶。。

dsjsjf 发表于 2015-12-12 21:59:06

学中医的来搞电子,也是拼了

超級稻草人 发表于 2015-12-13 10:16:27

不错,很好的想法,只得赞一个!先了解下原理,看看试着做一个吧。。。:)

mwx17 发表于 2015-12-13 13:26:48

dsjsjf 发表于 2015-12-12 21:59
学中医的来搞电子,也是拼了

其实真实故事是这样的,我是大学毕业后去道教学习中医,在某道观住了几年以后,想把中医职业医师证拿下来,所以现在30岁了去考了一个中医本科。
因为我在道教的缘故,所以内功等等都有学习,最近读了一下最近十几年中国中医大学和其他院校关于中医现代化和脉诊仪等项目的论文,发现他们的研究方向都错了,因为他们都是按照文字来研究,而从内证的角度来看,应该从人体真实规律,而不是从文字揣摩来研究,才是正确的。
所以我思考了很多时间以后,认为应该从最基础的脉学开始来重新定义科学版的中医才是最实际的路线。
他们做脉诊仪,是采集很多病人的脉形,然后来揣摩,然而从他们的论文来看,他们并没有获得有意义的数据来建立正确的模型。
如果有一个最简陋的脉诊仪,我可以从我自身来提供各种脉的案例,可重复,可以普适性,因为我摸别人的脉,就是把别人的脉在我身子里面模拟出来,然后观察我自己,各种病例性别都可以套用。
如果哪位愿意合作一起研究这个,无论最后是做成开源或者是私有项目,我都可以接受。我个人主要目的是做成这个研究。
这个还有很多后续的研究项目,我个人认为其中相当一部分都比较有经济价值。比如这个脉诊仪就可以做成穿戴式设备,等等。

dsjsjf 发表于 2015-12-14 19:30:27

不是太明白,只要使用到压力传感器,监测手腕处的压力变化的波形吗?

队长别开枪 发表于 2016-1-30 15:38:35

频率是多少,精度误差多少。

口圭口塞口丫 发表于 2016-1-30 16:46:45

mwx17 发表于 2015-12-13 13:26
其实真实故事是这样的,我是大学毕业后去道教学习中医,在某道观住了几年以后,想把中医职业医师证拿下来 ...

折服。 真是够拼的。

懒人漫游 发表于 2016-4-14 09:58:51

不明觉厉,帮顶
页: [1]
查看完整版本: 新人求助,adc采样频率问题