上一期点这里查看
测试用的是大约50KHz的方波,由Arduino Nano产生,程序如下:
void setup() {
pinMode(6,OUTPUT);
}
void loop() {
digitalWrite(6, HIGH);
delayMicroseconds(10);
digitalWrite(6, LOW);
delayMicroseconds(10);
}
1.周期调节
2.灵敏度调节。可以软件有损调节,也可以硬件电位器调节。这里展示的是软件调节。
3.触发
单片机部分请前往这里 查看详细制作过程。
除了单片机部分,还有ADC前的偏置电压放大电路需要制作。由于长时间不在家,库存的运放也找不到了。三极管凑活用。
电路图如下:
电阻取值计算:
a. 粗略取 Irc 大约 10mA,要使集电极电压大约为Vcc的 1/2, 即 1.65V,那么 Rc大约需要165欧, 随便找了个100欧的, 此时Irc为 1.65V/ 100欧 = 16.5mA。
b. 万用表测得三极管放大倍数是256倍。那么Irb = 16.5mA/256 = 64.5 uA。
c.三极管BE压降约0.7V, 则Urb = 3.3 - 0.7 =2.6V。
d . Rb = 2.6V/ 64.5uA = 40.3 K。
先搭棚焊接测试,如图:
2020-5-18更新运放电路图(358响应速度不太行,建议换成其他高速运放):
说明:
NE555产生负电压; U2A输入信号电压跟随;U2B基准电压跟随;U3A加法器,把输入信号和基准电压叠加;U3B放大,把信号放大到0~Vref。
2020-5-18 更新,添加程序框图:
单片机部分:
Matlab 及硬件 部分:
程序不复杂,就是不停地采样,然后发送。由于写的很乱,没来得及整理,就暂时不发出来了。
这里说一下一些初学者容易掉坑里的地方,我也被这些地方卡了挺久的:
1.无论做什么,先使能其时钟,不然任何操作都不会生效。
举几个例子:
//要操作IO,先使能IO时钟
rcu_periph_clock_enable(RCU_GPIOA);
//要使用ADC,先使能ADC时钟
rcu_periph_clock_enable(RCU_ADC0);
//要使用串口,先使能串口时钟
rcu_periph_clock_enable(RCU_USART0);
//DAC、计时器、DMA同理,详细内容参考官方文档
2.中断优先级及服务函数名
a. GD32F10x 系列单片机中断优先级由 nvic 库管理,官方文档中并没有中断优先级相关内容,导致看官方给的例程时候一脸问号。
使用中断务必配置优先级。比如我用到了ADC0中断,就需要调用如下函数进行优先级配置,具体函数定义可以看nvic文档:
void nvic_config(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3);
nvic_irq_enable(ADC0_1_IRQn, 1, 1);
}
b. 服务函数名在启动文件 startup_gd32f10x_hd.s 中有定义,如下图
比如ADC0 的中断服务函数可以写成
void ADC0_1_IRQHandler(){
adc_interrupt_flag_clear(ADC0, ADC_INT_EOC);//清除中断标志
ADC_DATA[DATA_COUNT++]=adc_regular_data_read(ADC0);//记录数据
adc_flag_clear(ADC0, ADC_FLAG_EOC);//清除转换完成标记
}
3.串口波特率
波特率需要一些特殊值,比如115200,921600等,可以计算得到,与时钟频率相关。不适当的波特率会导致无法通信。懒得计算也可以用已知可用值的整数倍,一般也是可用的。
4.串口传送12位的ADC数据
由于串口只能传送 8 位数,而ADC是12位的返回值,所有有两个选择。一是降低精度,二是自己写个简单的发送协议。
我采用第二种方案。
两帧 8 位数据一共 16 位, 把每帧的最后一位作为标记位, 剩余两个 7位可以用来传送 14位有效的数据。
接收端收到的 8 位二进制数据中, 最末位为 1 的是低7 位数据, 最末位为 0 的是高7 位数据,再进行拼接。
单片机(发送端)程序为:
while(usart_flag_get(USART0, USART_FLAG_TBE)==0);//等待前面的数据进入队列寄存器
usart_data_transmit(USART0,(ADC_DATA[DATA_COUNT]<<1)|0x0001);//发送低7位
while(usart_flag_get(USART0, USART_FLAG_TBE)==0);//等待前面的数据进入队列寄存器
usart_data_transmit(USART0,(ADC_DATA[DATA_COUNT]>>6)&0x00fe);//发送高7位
Matlab(接收端)程序为:
i=1;
k=0; % 最终输出的有效数据个数
while i<2999
i = i+1 ;
if ( bitand(data( i), uint8(1))==1 && bitand(data( i+1), uint8(1))==0)
dat(k) = bitand( bitshift( data( i) , -1, 'uint16'), 255 ) +data( i+1)*64 ;
i = i+1 ;
k=k+1;
end
end
5.Matlab串口通信
串口发送尽量用 fwrite(Com_obj, data); 而不要用 fprintf(Com_obj, data); 虽然后者有时候也可以运行,但有概率会导致单片机串口卡住,也可能是我的程序有bug。但改用前者就很稳定。
PS:本来打算用速率更高的USB进行数据传送的。但GD官方没有给USB函数库的说明, 自己看寄存器和USB协议一时半会儿也调试不出来,就暂时用串口了。
921600的波特率,每次传送3000个数据,可以达到 38FPS的刷新率也基本够用。
完。