基于GD32F310开发板的AD多通道交流采样计算
简介
很幸运能够有机会试用兆易公司的GD32F310开发板。本次使用中,我将使用开发板连接我之前的采样电路(之前自己做的板件)对交流电压信号进行采样,采用多通道规则连续采样模式,采样6路通道并进行计算,通过串口显示最后计算的数值进行比较分析。
软件环境
- 编程工具
keil-MDK 5.36(Windows10) - 参考资料
固件库-GD32F3x0_Firmware_Library_V2.2.0
GD32F310xx-数据手册-Rev1.1
GD32F3x0-用户手册-Rev2.5
GD32F3x0-固件库使用指南-Rev1.2
设计说明
主要是利用GD32F310这个核心板,通过本人已有的外围电路将继保测试仪的交流电压信号调理后采集到核心板上。交流电压信号50HZ,要求每个周期采样32个点,那么每个通道采样频率为1600Hz(32X50),通过DMA将数据放到缓冲区中,然后通过计算将数据通过串口发出。
硬件需求分析
本次用到的硬件有ADC、TIMER2、DMA、USART0等硬件资源。
- ADC : 6通道规则采样模式,定时器软件触发模式
- DMA : 通道CH0 将数据从AD转换寄存器到计算缓冲区
- TIMER2: 中断模式,定时时间为625ms
- USART0:波特率9600
注意:在GD32中好多外设命名都是从0开始的而stm32是从1开始的,比如stm32中没有usart0,timer0等,在写代码的时候我就想当然的写错了.
代码
`
//变量
extern u8 times;
u16 sample_date32;
u16 count_date[32];
u32 mvalue;
//ADC设置
void adc_set()
{
adc_deinit();
adc_dma_mode_enable(); //ADC_DMA模式使能
adc_vbat_disable(); //关闭Vbat监测
adc_channel_length_config(ADC_REGULAR_CHANNEL, 6);
//adc_discontinuous_mode_config(ADC_REGULAR_CHANNEL, 6); //规则通道组6
adc_special_function_config(ADC_SCAN_MODE, ENABLE); //使能扫描模式
adc_data_alignment_config(ADC_DATAALIGN_RIGHT); //数据向右对齐
//此处rank范围是0-15而stm32是1-16
adc_regular_channel_config(0, ADC_CHANNEL_1, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_regular_channel_config(1, ADC_CHANNEL_2, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_regular_channel_config(2, ADC_CHANNEL_3, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_regular_channel_config(3, ADC_CHANNEL_4, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_regular_channel_config(4, ADC_CHANNEL_5, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_regular_channel_config(5, ADC_CHANNEL_6, ADC_SAMPLETIME_7POINT5);//规则通道组设置
adc_resolution_config(ADC_RESOLUTION_12B); //配置ADC的分辨率
/* ADC trigger config */
adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE); //触发条件 none是不使用外部触发,只使用软件触发
adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE); //配置adc外部触发
adc_enable(); //先使能ADC再进行adc的校准
delay_1ms(10);
adc_calibration_enable();
}
//DMA设置
void dma_set()
{
dma_parameter_struct dma_init_struct;
dma_deinit(DMA_CH0); //复位DMA通道所有寄存器 ADC1 <--> channel0
/* initialize the parameters of DMA */
dma_struct_para_init(&dma_init_struct); //初始化结构体中的数据
/* DMA channel0 initialize */
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY; //外设寄存器to数据存储器
dma_init_struct.memory_addr = (u32)sample_date; //数据存储器地址
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE; //地址变化值
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT; //数据长度
dma_init_struct.number = 192; //数据存储器大小
dma_init_struct.periph_addr = (u32)&(ADC_RDATA); //AD1_RDATA;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE; //外设寄存器地址增加?
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; //外设数据长度
dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH; //优先级
dma_init(DMA_CH0, &dma_init_struct);
dma_circulation_enable(DMA_CH0);
dma_memory_to_memory_disable(DMA_CH0);
//DMA通道使能
dma_channel_enable(DMA_CH0);
}
//TIMER2设置
void timer2_set()
{
/* initialize TIMER0 */
timer_parameter_struct timer_initpara;
timer_deinit(TIMER2);
timer_initpara.prescaler = 71;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 624;//(0.020/16/0.000001)
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER2, &timer_initpara);
/* auto-reload preload enable */
timer_auto_reload_shadow_enable(TIMER2);
timer_interrupt_enable(TIMER2,TIMER_INT_FLAG_UP);
/* timer enable */
timer_enable(TIMER2);
}
//nvic设置
void nvic_set(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE0_SUB4);
nvic_irq_enable(TIMER2_IRQn, 0, 1);
}
//GPIO设置
void gpio_init()
{
//ad引脚
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_6);
//led控制引脚
gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
//usart0串口设置
/* connect port to USARTx_Tx */
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
/* connect port to USARTx_Rx */
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
/* configure USART Tx as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
/* configure USART Rx as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
}
//rcu设置
void rcu_set(void)
{
/* enable the LED GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA); // | RCU_ADC | RCU_DMA
rcu_periph_clock_enable(RCU_ADC);
rcu_periph_clock_enable(RCU_DMA);
rcu_periph_clock_enable(RCU_TIMER2);
/* config ADC clock */
//rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);
rcu_adc_clock_config(RCU_ADCCK_APB2_DIV8);
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART0);
}
/ retarget the C library printf function to the USART /
int fputc(int ch, FILE *f)
{
usart_data_transmit(USART0, (uint8_t)ch);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
return ch;
}
//主函数
int main(void)
{
float v22[6];
float temp22;
u8 cnt,i,j;
FlagStatus flag_value;
systick_config();
rcu_set();
gpio_init();
adc1_set();
dma_set();
timer_set();
usart0_set();
nvic_set();
while(1)
{
if(times > 31)
{
/* output a message on hyperterminal using printf function */
for(cnt = 0; cnt < 6;cnt++)
{
for(j = 0; j < 32; j++)
{
count_date[j] = sample_date[j][cnt];
}
v22[cnt]=fft(count_date);
temp22 = (float) v22[cnt] * PR2;
printf("\r\n Channel");//1 value: \r\n
printf("%d",cnt);
printf(" value: ");
printf("%6.1f",temp22);
/* wait for completion of USART transmission */
while(RESET == usart_flag_get(USART0, USART_FLAG_TC))
{
}
}
printf("\r\n ");//1 value: \r\n
while(RESET == usart_flag_get(USART0, USART_FLAG_TC))
{
}
times = 0;//重新开始采样标志位
}
delay_1ms(2000);
}
}
//中断函数
void TIMER2_IRQHandler(void)
{
if(RESET != timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP))
{
if(times >31)
{
}
else
{
times++;
adc_software_trigger_enable (ADC_REGULAR_CHANNEL); //启动一次AD采样
}
}
/* clear TIMER interrupt flag */
timer_interrupt_flag_clear(TIMER2,TIMER_INT_FLAG_UP);
}
`
计算结果
- 试验中发现如果在A1-A6不接下拉电阻的时候会有较大的浮动值,所以需要接下拉电阻。
- GD32F330K引脚限制没有将VSS和VSSA分开,也没有单独的ref引脚,如果做的产品对AD要求比较高可以找引脚多的型号。
- 外部电路是由隔离CT和调理电路组成,由于测试中很多都是飞线连接,所以会对结果有所影响,总体上是满足测试要求的,如果需要更精确的结果可以通过改进电路、绘制PCB板来达到。
小结
通过本次测试,GD32F310芯片完全能够满足公司部分产品的需求,且在运算速度和AD采样位数精度上有一个提高,相比之前用的进口单片机,综合性能上有很大提高。个人感觉比较适合做一些对数据量要求不是特别大的产品,当然32位,72Mhz的主频一般产品计算上已经足够用了。在当前形势下,即使相比于进口普通8位单片机,GD32F310的价格也具备优势,下面唯一的工作就是在实际的批量生产中去验证产品的稳定性,我也会继续努力,争取在新产品和产品替代上用上GD产品。本文有错误和欠妥之处望大家批评指正,共同进步。