/****************************************************************************** 版权所有: 文件名称: adc.c 文件版本: 01.01 创建作者: sunxi 创建日期: 2025-06-09 功能说明: adc驱动程序。 其它说明: 修改记录: */ /*------------------------------ 头文件 --------------------------------------- */ #include "bsp.h" #include "rt.h" #include "head.h" #include #include "rt_printf.h" // #include "shm_comm_packet.h" #include #include "shmem.h" /*------------------------------ 宏定义 --------------------------------------- */ #if CFG_BSP_DEBUG #define _ADC_DEBUG #endif #define AC_HZ_NORMAL 50.0 // 交流标准频率 #define ADC_DOT_BUF_SIZE ((CFG_ADC_CHANNEL) * (CFG_ADC_DOTS_PER_CHANNEL * 2) * 2) /*------------------------------ 全局变量 ------------------------------------- */ // AD片选地址,8、9、10、11槽对应CS2、CS1、CS3、CS4。 static int g_adc_slot2cs[EQU_SLOT_AC_NUM] = {0}; static u8 g_adc_chip; static u32 g_adc_range[EQU_SLOT_AC_NUM]; const u8 g_cs_channel_table[CFG_ADC_CHANNEL] = {1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16}; // 每个通道占用的空间:CFG_ADC_DOTS_PER_CHANNEL*2*2 = 128*2*2=512 // 通道数:CFG_ADC_CHANNEL+2= 64+2=66 // 总空间:512*18=9216=0x2400 // 保证首地址是buf长度两倍的倍数。以利用MAC计算的MASK寄存器来达到 // 循环buf的功能。注意每个通道buf的后半部分目前没有利用,以后可以用来干需要干的事情. // 通道加2,用作开入开出等状态保存。 // 不能使用分配的DMA内存,因为冷火实时linux平台不能使用动态内存映射。 // short (*g_adc_dots)[CFG_ADC_DOTS_PER_CHANNEL*2]; // s16 (*g_adc_dots)[CFG_ADC_DOTS_PER_CHANNEL*2] = (short (*)[CFG_ADC_DOTS_PER_CHANNEL*2])0x8ff00000; s16 (*g_adc_dots)[CFG_ADC_DOTS_PER_CHANNEL * 2]; struct timespec ts_adc_dots[CFG_ADC_DOTS_PER_CHANNEL]; // 时间戳 // 128点录波,32点计算 // 注意此buf由于是DMA类型,不能在实时中断中使用,只能在线程中使用。 // s16 g_adc_dots_rec[CFG_ADC_DOTS_PER_CHANNEL*4][CFG_ADC_CHANNEL]; s16 (*g_adc_dots_rec)[CFG_ADC_CHANNEL]; // 录波使用 u32 g_adc_dots_rec_dma; // 计算使用 // 直流量测量采样点BUF u16 g_adc_dots_dc[CFG_ADC_CHANNEL_DC][CFG_ADC_DOTS_PER_CHANNEL]; // ADC同步标志,表示ADC采样点BUFFER中的数据是否有效,AD7616转换的第一次数据也无效。 int g_adc_sync = -(CFG_ADC_DOTS_PER_CHANNEL + 1); // ADC点BUF的索引,当前索引下无值。 volatile unsigned long g_adc_dots_count; int g_adc_channel; unsigned long g_adZero = 14790; char g_adc_name[CFG_ADC_CHANNEL_ALL][20]; struct rt_stat g_stat_adc[CFG_ADC_CHANNEL_ALL]; // 跟踪频率 float g_freq = 50.0; // 直流量测量采样点BUF u16 g_adc_dots_dc[CFG_ADC_CHANNEL_DC][CFG_ADC_DOTS_PER_CHANNEL]; uint32_t g_yx_buf[2] = {0xffffffff, 0xffffffff}; // 预留64个遥信 int g_adc_stat_flag = 0; volatile unsigned int adc_wp; volatile unsigned int adc_rp; bool prt_flag; volatile unsigned long g_adc_dots_data_count; SHM_VER_T LuoHe_version; /*------------------------------ 函数声明 ------------------------------------- */ void dido_di_poll(SHM_SAMPLE_T *sample_data); /*------------------------------ 外部函数 ------------------------------------- */ // 设置各通道的参考电压 static int write_v_default(uint8_t no, uint8_t *data) { int ret = 0; int cnt = 0; if (data == NULL) return -1; while (1) { // ret = shm_comm_packet_write(SHM_ADDR_D_RFV_1+(SHM_ADDR_D_RFV_2-SHM_ADDR_D_RFV_1)*no, data, sizeof(SHM_RFV_T)); if (ret > 0) { break; } if (++cnt > 3) break; usleep(30); } return ret; } int adc_set_v_default(int no, u32 value) { int ret = 0; SHM_RFV_T v; v.rc = value; ret = write_v_default(no, (uint8_t *)&v); return ret; } void adc_get_range_config(int no, int slot) { int i, scale, channel, shift; u32 rc; rc = 0; for (i = 0; i < EQU_SLOT_AC_CHN; i++) { channel = equ_get_ac_channel(slot, i) & 0x0f; // 确定需要移位的数量 // 双数是通道A,在低16位 // 单数是通道B,在高16位 shift = (channel / 2) * 2; if ((channel & 0x01)) { shift += 16; } if (channel == AD7616_DC0_CHANNEL || channel == AD7616_DC1_CHANNEL) { rc |= 2 << shift; // 5V continue; } scale = equ_get_ac_scale(slot, i); switch (scale) { case EQU_SCALE_PT_DEFAULT: case EQU_SCALE_PT_120K: case EQU_SCALE_PT_172K: case EQU_SCALE_PT_204K: case EQU_SCALE_CT_10A_51: case EQU_SCALE_CT_10A_120: rc |= 1 << shift; // 2.5V break; case EQU_SCALE_PT_102K: case EQU_SCALE_PT_120K_604: case EQU_SCALE_PT_264V_3V53: case EQU_SCALE_CT_100A_3V53: case EQU_SCALE_CT_DEFAULT: case EQU_SCALE_CT_10A_249: case EQU_SCALE_CT_10A_604: case EQU_SCALE_EVT_3V25_100V: case EQU_SCALE_PT_400V_3V53: case EQU_SCALE_CT_20A_3V53: rc |= 2 << shift; // 5V break; case EQU_SCALE_EVT_6V50_100V: case EQU_SCALE_ECT_1V_1A: case EQU_SCALE_ECT_0V2_1A: // 零序电子CT采用2.5V的范围 rc |= 3 << shift; // 10V break; } } adc_set_v_default(no, rc); mb_notice_RfV_update(0); } static int get_adc_wp(void) { WP_T wp; int ret = 0; int cnt = 0; while (1) { ret = shm_comm_packet_read(SHM_ADDR_U_WP, sizeof(wp), (uint8_t *)&wp, sizeof(wp)); if (ret > 0) { return wp.write_flag_A; } if (++cnt > 3) break; usleep(30); } return ret; } static int get_adc_sample(uint32_t addr, int index, uint8_t *out_data) { SHM_SAMPLE_T sample; int ret = 0; int cnt = 0; if (out_data == NULL) return -1; while (1) { ret = shm_comm_packet_read_noABcrc(addr + index * (sizeof(sample)), sizeof(sample), out_data, sizeof(sample)); if (ret > 0) { break; } if (++cnt > 3) break; usleep(30); } return ret; } static int get_e907_version(SHM_VER_T *par) { SHM_VER_T ver; int ret = 0; int cnt = 0; if (par == NULL) return -1; while (1) { ret = shm_comm_packet_read(SHM_ADDR_U_VER, sizeof(ver), (uint8_t *)&ver, sizeof(ver)); if (ret > 0) { par->ver = ver.ver; par->v_crc = ver.v_crc; strcpy(par->time, ver.time); return 0; } if (++cnt > 3) break; usleep(30); } return -1; } int adc_init(void) { int ret = 0; int i; /* ret = get_adc_wp(); if(ret < 0) { rt_printf("get_adc_wp 返回错误值,ret=%d!\r\n",adc_wp); return -1; } adc_wp = adc_rp = ret; */ memset(&LuoHe_version, 0, sizeof(LuoHe_version)); g_adc_dots_count = 0; prt_flag = false; // 采样计算点BUF初始化 // 32;周波数 g_adc_dots = malloc(ADC_DOT_BUF_SIZE); if (g_adc_dots == NULL) { rt_printf("g_adc_dots 分配失败!\r\n"); return -1; } memset(g_adc_dots, 0x55, ADC_DOT_BUF_SIZE); g_adc_dots_data_count = 0; // 录波点BUF初始化 // 32;周波数 g_adc_dots_rec = malloc(CFG_ADC_CHANNEL * 2 * ADC_REC_DOTS_CHANNEL); if (g_adc_dots_rec == NULL) { rt_printf("g_adc_dots_rec 分配失败!\r\n"); rt_free(g_adc_dots); g_adc_dots = NULL; return -1; } // 初始化统计变量 for (i = 0; i < CFG_ADC_CHANNEL_ALL; i++) { sprintf(g_adc_name[i], "adc%02d[%08lx]", i, (u32)g_adc_dots + i * sizeof(g_adc_dots[0])); rt_stat_init(&g_stat_adc[i], g_adc_name[i]); } #if 0 for(i=0; i= SHM_ADC_WR_NUM) { adc_wp = 0; } if (adc_rp >= SHM_ADC_WR_NUM) { adc_rp = 0; } while (adc_wp != adc_rp) { opt = ((g_adc_dots_data_count & (ADC_REC_SAMPLE_RATE - 1)) == 0) ? TRUE : FALSE; ret = get_adc_sample(SHM_ADDR_U_ADC_1, adc_rp, (uint8_t *)&sample_data[0]); ret = get_adc_sample(SHM_ADDR_U_ADC_2, adc_rp, (uint8_t *)&sample_data[1]); if (ret < 0) { if (++cnt > 3) { rt_printf("get_adc_sample 返回错误值, ret=%d!\r\n", ret); break; } continue; } // 保存yx值 g_yx_buf[0] = sample_data[0].yx_buf[0]; g_yx_buf[1] = sample_data[1].yx_buf[1]; if (!recv_init) { recv_init = 1; } // 标准化DTU遥信已经经过开入子板处理防抖 // dido_di_poll(&sample_data[0]); for (i = 0; i < 16; i++) { g_adc_dots_rec[g_adc_dots_index_rec][i] = sample_data[0].sample_buf[i]; if (opt == TRUE) { g_adc_dots[i][g_adc_dots_index] = sample_data[0].sample_buf[i]; ts_adc_dots[g_adc_dots_index] = sample_data[0].ts; } } for (i = 16; i < CFG_ADC_CHANNEL; i++) { g_adc_dots_rec[g_adc_dots_index_rec][i] = sample_data[1].sample_buf[i - 16]; if (opt == TRUE) { g_adc_dots[i][g_adc_dots_index] = sample_data[1].sample_buf[i - 16]; ts_adc_dots[g_adc_dots_index] = sample_data[1].ts; } } g_adc_dots_dc[0][g_adc_dots_data_count & CFG_ADC_DOTS_MASK] = sample_data[0].sample_buf[AD7616_DC0_CHANNEL]; // g_adc_dots_dc[1][g_adc_dots_data_count&CFG_ADC_DOTS_MASK] = sample_data.sample_buf[AD7616_DC0_CHANNEL]; if (opt == TRUE) { index = g_adc_dots_index; // 统计数据 if (g_adc_stat_flag) { for (i = 0; i < CFG_ADC_CHANNEL; i++) { rt_stat_in(&g_stat_adc[i], (unsigned short)(g_adc_dots[i][index] + 32768)); } } // 通道取反 for (i = 0; i < g_equ_adc_inv_num; i++) { g_adc_dots[g_equ_adc_inv[i]][index] = -(g_adc_dots[g_equ_adc_inv[i]][index]); } // 全部BUFFER都是新数据,才能算同步好。 if (g_adc_sync < 1) { g_adc_sync++; } // 索引值加一 g_adc_dots_count++; // 保护计数+突变量 { dTCounter++; if (dTCounter % 8 == 0) { prt_flag = true; } // 突变量 if (g_adc_sync == 1 && g_sw_init == 1) { protect_tbl_qd(); } } } g_adc_dots_data_count++; if (++adc_rp >= SHM_ADC_WR_NUM) adc_rp = 0; } return 0; } static void updata_adc(struct t_shmdata_adc *sample_data0, uint16_t us_adc_dot_index0) { int i = 0; int index = 0; static int mod = 0; int opt = ((g_adc_dots_data_count & (ADC_REC_SAMPLE_RATE - 1)) == 0) ? TRUE : FALSE; // uint32_t ul_adc_idx_1 = 0, ul_adc_idx_2 = 0; // 每片16路 for (uint8_t j = 0; j < CFG_ADC_CHANNEL; j++) { if (AD7616_DC0_CHANNEL == j) { g_adc_dots_dc[0][g_adc_dots_data_count & CFG_ADC_DOTS_MASK] = sample_data0->data.usa_sample_dots[AD7616_DC0_CHANNEL][us_adc_dot_index0]; } else if (AD7616_DC1_CHANNEL == j) { g_adc_dots_dc[1][g_adc_dots_data_count & CFG_ADC_DOTS_MASK] = sample_data0->data.usa_sample_dots[AD7616_DC0_CHANNEL][us_adc_dot_index0]; } else { g_adc_dots_rec[g_adc_dots_index_rec][j] = sample_data0->data.usa_sample_dots[j][us_adc_dot_index0]; // ul_adc_idx_1 = g_adc_dots_index_rec; if (opt == TRUE) { g_adc_dots[j][g_adc_dots_index] = sample_data0->data.usa_sample_dots[j][us_adc_dot_index0]; // ul_adc_idx_2 = g_adc_dots_index; } } } if (opt == TRUE) { index = g_adc_dots_index; // 统计数据 if (g_adc_stat_flag) { for (i = 0; i < CFG_ADC_CHANNEL; i++) { rt_stat_in(&g_stat_adc[i], (unsigned short)(g_adc_dots[i][index] + 32768)); } } // 通道取反 for (i = 0; i < g_equ_adc_inv_num; i++) { g_adc_dots[g_equ_adc_inv[i]][index] = -(g_adc_dots[g_equ_adc_inv[i]][index]); } // 全部BUFFER都是新数据,才能算同步好。 if (g_adc_sync < 1) { g_adc_sync++; } // 索引值加一 g_adc_dots_count++; // 保护计数+突变量 { dTCounter++; if (dTCounter % 8 == 0) { prt_flag = true; } // 突变量 if (g_adc_sync == 1 && g_sw_init == 1) { protect_tbl_qd(); } } } #ifdef XDL_ZT xdl_tbl_qd(mod); #endif mod++; mod &= (CFG_ADC_MOD_NUM - 1); g_adc_dots_data_count++; } /** * @brief 新方式获取共享内存数据 * * @param mod * @return int * @author zhl */ int adc_isr_new(int mod) { int ret0 = 0; struct t_shmdata_adc sample_data0 = {0}; static volatile uint16_t smus_sample_read_index0 = 0; /* 读取adc1索引 */ ret0 = shm_packet_read_v2(SHM_ADDR_W_U_ADC_1, sizeof(sample_data0), (uint8_t *)&sample_data0, sizeof(sample_data0)); // printf("ret0=%d, sample_idx=%d, read_index=%d, us_updata=%d, us_op=%d, us_op_bk=%d\n", // ret0, sample_data0.data.us_sample_idx, smus_sample_read_index0, sample_data0.us_updata, sample_data0.us_op, sample_data0.us_op_bk); if (0 > ret0) { return -1; } if (ret0 > 0 && (smus_sample_read_index0 != sample_data0.data.us_sample_idx)) { uint16_t us_adc_dot_num0 = 0; if (sample_data0.data.us_sample_idx > smus_sample_read_index0) { us_adc_dot_num0 = sample_data0.data.us_sample_idx - smus_sample_read_index0; } else { us_adc_dot_num0 = (sample_data0.data.us_sample_idx + TC_ADC_DOTS_PER_CHANNEL) - smus_sample_read_index0; } // printf("us_adc_dot_num0:%d\n", us_adc_dot_num0); // us_adc_dot_num0 与 us_adc_dot_num1的数量一般相同 for (uint16_t i = 0; i < us_adc_dot_num0; i++) { /* 注意采样下标索引不能超过采样缓存 TC_ADC_DOTS_PER_CHANNEL */ uint16_t us_adc_dot_index0 = (smus_sample_read_index0 + i) % TC_ADC_DOTS_PER_CHANNEL; // test zhl // int32_t physical_value0 = (int16_t)(sample_data0.data.usa_sample_dots[9][us_adc_dot_index0]); // printf("%d, ", physical_value0); // int32_t physical_value1 = (int16_t)(sample_data1.data.usa_sample_dots[1][us_adc_dot_index1]); // printf("%d, ", physical_value1); updata_adc(&sample_data0, us_adc_dot_index0); } smus_sample_read_index0 = (smus_sample_read_index0 + us_adc_dot_num0) % TC_ADC_DOTS_PER_CHANNEL; } return 0; } /****************************************************************************** 函数名称: adc_slot_is_ok 函数版本: 01.01 创建作者: sunxi 创建日期: 2016-02-01 函数说明: 检查对应槽的ADC芯片是否安装或损坏。 参数说明: slot: 0~3;从g_ac_slot_begin开始 返回值: 成功返回0. 修改记录: */ int adc_slot_is_ok(int slot) { if ((u32)slot >= EQU_SLOT_AC_NUM) { return 0; } if (g_adc_chip & (1 << g_adc_slot2cs[slot])) { return 1; } return 0; } /****************************************************************************** 函数名称: adc_fourier_address 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-06-26 函数说明: 返回指定的通道进行傅立叶计算的首地址。首地址保证4bytes对齐。 参数说明: channel:ADC的通道,取值0~7。 index: 要进行傅立叶计算的32点中的最后一个点的索引。这么设置是为了和 g_adc_dots_index匹配。保证g_adc_dots_index可以直接作为参数传进来。 返回值: 成功返回0. 修改记录: */ #if 0 short * adc_fourier_address(int channel,int index) { short *p; //{index + CFG_ADC_DOTS_PER_CHANNEL}是为了确保索引在相应的范围之内. index = index + CFG_ADC_DOTS_PER_CHANNEL - CFG_ADC_DOTS_PER_PERIOD; //使用偶数索引,保证4bytes对齐 index &= (~0x1); //得到首地址,注意地址必须和CFG_ADC_BUF_MASK相与。 p = (short *)&g_adc_dots[channel][index]; p = (short *)((unsigned int)p&CFG_ADC_BUF_MASK); return p; } #else short sam_buf[CFG_ADC_CHANNEL][CFG_ADC_DOTS_PER_PERIOD]; short *adc_fourier_address(int channel, int index) { short *p; int i; if (index >= CFG_ADC_DOTS_PER_PERIOD) { index = index - CFG_ADC_DOTS_PER_PERIOD; index &= (~0x1); p = (short *)&g_adc_dots[channel][index]; } else { index = CFG_ADC_DOTS_PER_CHANNEL - CFG_ADC_DOTS_PER_PERIOD + index; index &= (~0x1); for (i = 0; i < CFG_ADC_DOTS_PER_CHANNEL - index; i++) { sam_buf[channel][i] = g_adc_dots[channel][index + i]; } for (i = 0; i < CFG_ADC_DOTS_PER_PERIOD + index - CFG_ADC_DOTS_PER_CHANNEL; i++) { sam_buf[channel][i + CFG_ADC_DOTS_PER_CHANNEL - index] = g_adc_dots[channel][i]; } p = sam_buf[channel]; } return p; } #endif /****************************************************************************** 函数名称: adc_fourier_address_half 函数版本: 01.01 创建作者: sunxi 创建日期: 2008-06-26 函数说明: 返回指定的通道进行傅立叶计算的首地址。首地址保证4bytes对齐。 参数说明: channel:ADC的通道,取值0~7。 index: 要进行傅立叶计算的32点中的最后一个点的索引。这么设置是为了和 g_adc_dots_index匹配。保证g_adc_dots_index可以直接作为参数传进来。 返回值: 成功返回0. 修改记录: */ short *adc_fourier_address_half(int channel, int index) { short *p; //{index - (CFG_ADC_DOTS_PER_PERIOD - 1)}是因为索引下有实际的采样值,所以不是 // index - CFG_ADC_DOTS_PER_PERIOD. //{index + CFG_ADC_DOTS_PER_CHANNEL}是为了确保索引在相应的范围之内. index = index + CFG_ADC_DOTS_PER_CHANNEL - (CFG_ADC_DOTS_PER_PERIOD / 2 - 1); // 使用偶数索引,保证4bytes对齐 index &= (~0x1); // 得到首地址,注意地址必须和CFG_ADC_BUF_MASK相与。 p = (short *)&g_adc_dots[channel][index]; p = (short *)((unsigned int)p & CFG_ADC_BUF_MASK); return p; } int adc_stat_printf(void) { int i; unsigned long avg = 0; struct rt_stat *stat; if (g_adc_stat_flag == 0) { g_adc_stat_flag = 1; rt_printf("\r\n!!!ADC采样统计启动, 执行reset命令关闭。!!!\r\n"); } rt_printf("\r\n频率跟踪:%fHZ.\r\n", g_freq); rt_printf("g_adc_chip:0x%02x.\r\n", g_adc_chip); for (i = 0; i < EQU_SLOT_AC_NUM; i++) { rt_printf("槽[%02lu]: 是否有对应芯片:%d,输入范围:0x%08lx.\r\n", g_ac_slot_begin + i, adc_slot_is_ok(i), g_adc_range[g_adc_slot2cs[i]]); } rt_printf("\r\n[ADC中断周期统计]\r\n"); rt_printf("name\t\t\tmin\tmax\tavg\tsum\t\tcnt\n"); // rt_stat_printf(&g_stat_adc_time); rt_printf("\r\n[ADC抖动统计]\r\n"); rt_printf("name\t\t\tmin\tmax\tdif\tavg\tsum\t\tcnt\n"); rt_printf("\r\n"); for (i = 0; i < CFG_ADC_CHANNEL_ALL; i++) { stat = &g_stat_adc[i]; if (stat->cnt) { avg = stat->sum / stat->cnt; } rt_printf("%-24s%ld\t%ld\t%ld\t%ld\t%-16ld%ld\r\n", stat->name, stat->min, stat->max, stat->max - stat->min, avg, stat->sum, stat->cnt); } return 0; } int adc_stat_reset(void) { int i; g_adc_stat_flag = 0; for (i = 0; i < CFG_ADC_CHANNEL_ALL; i++) { rt_stat_init(&g_stat_adc[i], g_stat_adc[i].name); } // rt_stat_init(&g_stat_adc_time,g_stat_adc_time.name); return 0; } #if 0 static int get_adc_dc(uint8_t *out_data) { int ret = 0; int cnt = 0; if(out_data == NULL) return -1; while(1) { ret = shm_comm_packet_read(SHM_ADDR_U_DC, sizeof(SHM_DC_T), out_data, sizeof(SHM_DC_T)); if(ret > 0) { break; } if(++cnt > 3) break; usleep(30); } return ret; } #endif float dc_get(unsigned int index) { u32 i, v; float f; if (index >= CFG_ADC_CHANNEL_DC) return 0.0; v = 0; for (i = 0; i < CFG_ADC_DOTS_PER_CHANNEL; i++) { v += (u16)g_adc_dots_dc[index][i]; } f = (float)v / CFG_ADC_DOTS_PER_CHANNEL; // 乘理论系数 f /= 129.791f; return f; } /*------------------------------ 测试函数 ------------------------------------- */ #ifdef _ADC_DEBUG int adc_test(void) { return 0; } #endif /*------------------------------ 文件结束 ------------------------------------- */