AD采集–转换时序
(1)CONVST(A/B)由高拉低后,BUSY会被拉高,当BUSY从高到低,完成一次转换。
总结:
开启AD采集的信号 : CONVST(A/B)由高拉低(低电平至少保持45ns)
开始转换信号 : BUSY被拉高
转换完成信号 : BUSY由高被拉低
(2)当转换完成(判断BUSY从高到低),然后把CS拉低可以开始读取数据(注意读取完成后,CS拉回高)
使用的是SPI读取,每次读取8位,时序如下:
总结:
①CS 拉低,开始读取
②一个数据16位,先高8位,后低8位
③读取一个完整的字节,需要连续读取2次(每次读8位)。
④总共8个通道,从第一通道到第8通道,连续顺序的传输,所以:
通道n的高8位就是第2*(n-1)+1次读取,低8位是2*(n-1)+2次读取(注:前提是spi传输函数i每次读8位)
开始采集信号 : CONVA 低电平,可以有2种控制方法:
(1) 配置为GPIO_Output推挽输出模式,手动控制输出电平的高低。当需要转换的时候,把电平拉低,稍微延时(至少45ns),再拉高即可。
缺点:不好控制采集信号的时间间隔。
(2) 配置为PWM模式
CONVA所接引脚配置为PWM模式,PWM产生波形,可以控制好AD的采样频率—采样频率即为PWM波形的频率。(建议PWM模式)
BUSY是下降沿为一次转换完成。所以如何思路就是判断BUSY是否为下降沿。2种方式:
(1) 因为是要判断BUSY引脚的电平状态,所以BUSY是要配置为GPIO_Input为输入模式。需要注意的是,用这种方式,需要轮询去判断BUSY是否为下降沿,注意,是判断是下降沿,所以不能只是单纯判断BUSY引脚的电平为低,必须设有标志,可以判断出是否为下降沿。
(2) 把BUSY引脚设置为下降沿触发的中断。这样当有下降沿的时候,再判断BUSY引脚是否为低电平可以了。
(1) 转换完成后(BUSY下降沿),拉低CS,进行读取。
(2) 读取完成后,拉高CS。
我采用的是STM32F446RETX
配置TIM3通道4为PWM模式。
重载值与分频系数,决定了波形的频率:计算方法为:
PWM波频率 = (总线时钟频率/分频系数)/自动重载值
例如:所在总线的时钟频率是90MHz,设置分频系数为9,自动重装载为1000,那么PWM频率为:(90M/9=10M)/1000=10KHz
注意:初始化后,一定要加入以下这句,开启通道(否则只是进行了配置,没有开启)
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_4);//开启PWM通道4 --- 注意要开启通道,否则只是配置,并不会运行PWM
(1)开启了PWM后,那么就会有固定频率采集开始信号,即CONVA 低电平。
(2)等待BUSY从高到低:
以下代码是使用轮询方式,不断询问BUSY是否由高到低,需要有标志位判断(若BUSY使用下降沿中断触发,则不需要标志位)
u8 i;
static u8 st = DISABLE;//标志位,用来判断是否从高到低
/* 等待转换完成,当BUSY为0时,AD转换完成 */
if(BUSY ==0 )
{
if(st== DISABLE)//BUSY是0,而且上一个状态必须是高电平,才满足条件
{
.....
}
else
st=DISABLE; //如果 BUSY不为0,则 st = DISABLE
(3)读取AD信息(在CS = 0 和 CS =1之间,):
使用SIP通信,SPI每次读取的是8个字节,那么, 读取2次才算读完一个通道。共8个通道,那么就要读取16次。(数据是先高8位,后低8位)
if(BUSY ==0 )
{
if(st== DISABLE)
{
AD_CS_0(); /*SPI片选 CS = 0 */
/*在此区间读取AD采集信息*/
/*CH_NUM是需要读取的通道数 每次读取8位,一个数据是16位,先高位*/
for (i = 0; i < CH_NUM; i++)
{
s_adc_now[i] = 0;
s_adc_now[i] = SPI1_ReadWriteByte(0);
s_adc_now[i] <<= 8;
s_adc_now[i] = s_adc_now[i] | SPI1_ReadWriteByte(0);
}
AD_CS_1(); /* CS= 1 */
}
}
else
st=DISABLE;
(4)转换为电压。
注意:16位中,有1位是符号位。
/* 开始转换为电压信号
32767 = 5000mV , 这是理论值,实际可以根据5V基准的实际值进行公式矫正 (有一位是符号位,所以除以 32767 而不是 65535
volt[i] = ((int16_t)dat[i] * 5000) / 32767; 计算实际电压值(近似估算的),如需准确,请进行校准 */
s_volt[i] = (s_adc_now[i] * 5000) / 32767;