/* * @Author: xxx * @Date: 2023-08-04 08:48:56 * @LastEditors: xxx * @LastEditTime: 2023-08-25 14:38:40 * @Description: 此文件为驱动测试用例,用于板卡驱动测试 * Copyright (c) 2023 by xxx, All Rights Reserved. */ #include "lib.h" #include "test_bsp.h" #include "entity.h" #include "board.h" #include "hart.h" #include "rtc_rx8010.h" #include "eeprom_m95.h" #include "eeprom_fm24.h" #include #include "flashdb.h" #define ADC_MIN 100 // 有效ADC最小值 #define ADC_MAX 4095 // 有效ADC最大值 #define VOL_DIF 0.5 // 电压稳态误差 uint8_t test_buf[10]; /** * @brief 测试GPIO是否正常工作 IP输出使能 : VIP_EN_GPIO_Port, VIP_EN_Pin IP大电流输出使能 : IP_H_PWR_GPIO_Port, IP_H_PWR_Pin * @param {GPIO_TypeDef} *port * @param {uint16_t} pin * @return {*} */ BOOL test_gpio(GPIO_TypeDef *port, uint16_t pin) { uint8_t ret = 0; // 临时变量,用于保存返回值 GPIO_SET(port, pin); // 将指定GPIO引脚设置为高电平 delay_tick(10); // 等待10个时钟周期 ret = GPIO_READ(port, pin); // 读取当前GPIO引脚的状态 if (ret != 1) { return FALSE; // 如果读取到的状态不是1,表示设置失败 } GPIO_RESET(port, pin); // 将指定GPIO引脚设置为低电平 delay_tick(10); // 等待10个时钟周期 ret = GPIO_READ(port, pin); // 读取当前GPIO引脚的状态 if (ret != 0) { return FALSE; // 如果读取到的状态不是0,表示设置失败 } return TRUE; // 设置成功 } /** * @brief 这是一个用于测试ADC读取功能的函数 测试ADC,通道使用如下: 回路电流 : ADC_LOOP_CHANNEL 阀位反馈 : ADC_PSB_CHANNEL 阀位反馈-小回路 : ADC_IPSB_CHANNEL 气源压力 : ADC_BP_CHANNEL A路压力 : ADC_BPA_CHANNEL B路压力 : ADC_BPB_CHANNEL * @param {adc_num_t} channel - 指定ADC通道,通道使用如下: * @param {uint16_t} *adc - 存储读取到的ADC值的指针 * @return {*} - 返回一个布尔值,表示读取操作是否成功 */ BOOL test_adc(uint8_t chan, uint16_t *adc) { uint16_t tmp = 0; // 临时变量,用于存储读取到的ADC值 tmp = adc_result_average(ADCS_1, chan); // 读取指定ADC通道的值 if ((tmp >= ADC_MIN) && (tmp <= ADC_MAX)) // 判断读取到的值是否在允许的范围内 { *adc = tmp; // 读取成功,将读取到的值存储到*adc指针指向的位置 return TRUE; // 返回成功标志 } return FALSE; // 返回失败标志 } /** * @brief 这是一个用于测试IP输出的函数。 * @param {uint16_t} value - 测试的DAC设定值。 * @return {*} - 测试结果,TRUE表示测试通过,FALSE表示测试失败。 */ BOOL test_ip_putput_dac(uint16_t value) { uint16_t dac = value; float32 target_vol; float32 target_cur; uint16_t adc; float32 actual_vol; // 设置DAC输出设定值 dac = value; // dac_output(dac); // 计算理论电流ma target_cur = ip_dac2current(dac); // 计算理论电压v target_vol = target_cur * 10 * 101 / 1000; // 读取实际ip输出 adc = adc_result_average(ADCS_1, ADC_VIP_CHANNEL); // 计算实际电压v actual_vol = (float32)adc / 4096 * VREF_VALUE / 1000; // 判断实际电压与理论电压的差值是否小于允许的误差范围 if (fabs(target_vol - actual_vol) < VOL_DIF) { return TRUE; } // 测试失败 return FALSE; } /** * @brief 这是一个用于测试IP输出的函数。 * @param {uint16_t} duty 占空比 * @return {*} * @note */ BOOL test_ip_putput_pwm(float32 duty_percent) { uint16_t adc; float32 target_vol; float32 actual_vol; target_vol = 6.2f * duty_percent; // 读取实际ip输出 adc = adc_result_average(ADCS_1, ADC_VIP_CHANNEL); // 计算实际电压v actual_vol = (float32)adc / 4096 * VREF_VALUE / 1000; // 判断实际电压与理论电压的差值是否小于允许的误差范围 if (fabs(target_vol - actual_vol) < VOL_DIF) { return TRUE; } return FALSE; } /** @brief 这是一个用于测试DC/DC转换的函数,它接收一个float32类型的指针参数vol,用于存储转换后的电压值。 @param {float32} *vol @return {*} */ BOOL test_dcdc(float32 *vol) { uint16_t tmp_adc1; float32 tmp_vol1; // 读取ADC值 tmp_adc1 = adc_result_average(ADCS_1, ADC_DCDC_CHANNEL); // 计算实际电压v tmp_vol1 = (float32)tmp_adc1 / 4096 * VREF_VALUE / 1000 * 2; *vol = tmp_vol1; return TRUE; } /** * @brief 测试回路电流 * @return {*} */ BOOL test_loop_current(float32 *cur) { uint16_t tmp_adc; float32 tmp_cur; // 读取ADC值 tmp_adc = adc_result_average(ADCS_1, ADC_LOOP_CHANNEL); // 计算回路电流 tmp_cur = loop_current_convert(tmp_adc); *cur = tmp_cur; return TRUE; } /** @brief 这是一个用于测试温度传感器的函数,它接收一个float32类型的指针参数temp_f,用于存储转换后的温度值。 @param {float32} *temp_f @return {*} */ BOOL test_temperature(float32 *temp_f) { // 获取当前温度 *temp_f = get_temperature(); return TRUE; } /** * @brief 这是一个用于测试RTC功能的函数,它接受两个参数:一个表示操作类型的uint8_t opt和一个指向rtc_date类型的指针data。 * @param {uint8_t} opt - 操作类型,可以是0(设置时间)或1(读取时间)。 * @param {rtc_date} *data - 包含时间信息的rtc_date结构体的指针。 * @return {*} - 操作成功返回TRUE,否则返回FALSE。 */ BOOL test_rtc(rtc_opt_e opt, rtc_date *data) { uint8_t tmp[7]; if (opt == 0) // 如果操作类型为0(设置时间),则调用rtc_set_clock_time函数设置时间 { rtc_set_clock_time(data); return TRUE; } else if (opt == 1) // 如果操作类型为1(读取时间),则调用rtc_get_clock_time函数读取时间并将其存储在tmp数组中 { rtc_get_clock_time(tmp); data->year = tmp[6]; // 将年存储在data结构体的year字段中 data->month = tmp[5]; // 将月存储在data结构体的month字段中 data->day = tmp[4]; // 将日存储在data结构体的day字段中 data->weekday = tmp[3]; // 将星期存储在data结构体的weekday字段中 data->hour = tmp[2]; // 将时存储在data结构体的hour字段中 data->minute = tmp[1]; // 将分存储在data结构体的minute字段中 data->second = tmp[0]; // 将秒存储在data结构体的second字段中 // 判断rtc_date 数据是否合法 if ((data->year > 0x99) || (data->month > 0x12) || (data->day > 0x31) || (data->weekday > SAT) || (data->hour > 0x23) || (data->minute > 0x59) || (data->second > 0x59)) { return FALSE; // 如果数据不合法,返回FALSE } return TRUE; } return FALSE; // 如果操作类型不在0和1之间,返回FALSE } BOOL test_eeprom_lc02b(uint16_t addr, uint8_t *buf, uint16_t len) { uint8_t i = 0; osel_memset((uint8_t *)test_buf, 0, 10); // eeprom_lc02b_write(addr, buf, len); // 向EEPROM写入数据 delay_ms(10); // 芯片要求,必须等待10ms // eeprom_lc02b_read(addr, test_buf, len); // 从EEPROM读取数据到tmp数组中 for (i = 0; i < len; i++) { if (test_buf[i] != *(buf++)) // 逐个比较buf和tmp数组中的数据 { return FALSE; // 如果发现有不相等的数据,返回FALSE } } return TRUE; // 如果所有数据都相同,返回TRUE } /** * @brief SPI EEPROM写入测试 * @return {*} */ BOOL test_eeprom_m95_write(m95_number_e num, uint8_t *buf, uint16_t len) { eeprom_m95_write(num, _M95_SIZE - 64, buf, len); // 向EEPROM写入数据 delay_ms(10); // 等待一段时间 return TRUE; // 返回TRUE } /** * @brief SPI EEPROM读取测试 * @return {*} */ BOOL test_eeprom_m95_read(m95_number_e num, uint8_t *buf, uint16_t len) { eeprom_m95_read(num, _M95_SIZE - 64, buf, len); // 从EEPROM读取数据 delay_ms(10); // 等待一段时间 return TRUE; // 返回TRUE } /** * @brief 这是一个用于测试外部EEPROM的函数,它接受三个参数: * @param {uint16_t} addr - 要写入的地址。 * @param {uint8_t *} buf - 要写入的数据缓冲区。 * @param {uint16_t} len - 要写入的数据长度。 * @return {*} * @retval TRUE - 如果写入和读回的数据相同,则返回TRUE。 * @retval FALSE - 如果写入和读回的数据不同,则返回FALSE。 */ BOOL test_eeprom_m95(m95_number_e num, uint16_t addr, uint8_t *buf, uint16_t len) { uint8_t i = 0; osel_memset((uint8_t *)test_buf, 0, 10); eeprom_m95_write(num, addr, buf, len); // 向EEPROM写入数据 delay_ms(10); // 等待一段时间 eeprom_m95_read(num, addr, test_buf, len); // 从EEPROM读取数据到tmp数组中 for (i = 0; i < len; i++) { if (test_buf[i] != *(buf++)) // 逐个比较buf和tmp数组中的数据 { return FALSE; // 如果发现有不相等的数据,返回FALSE } } return TRUE; // 如果所有数据都相同,返回TRUE } static BOOL test_flashdb_kv(void) { static uint32_t boot_count = 0; /* default KV nodes */ static struct fdb_default_kv_node default_kv_table[] = { {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */ }; /* KVDB object */ static struct fdb_kvdb kvdb = {0}; fdb_err_t result; struct fdb_default_kv default_kv; default_kv.kvs = default_kv_table; default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]); /* set the lock and unlock function if you want */ // fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)fdb_lock); // fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)fdb_unlock); /* Key-Value database initialization * * &kvdb: database object * "env": database name * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. * Please change to YOUR partition name. * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully. * NULL: The user data if you need, now is empty. */ result = fdb_kvdb_init(&kvdb, "env", "KVDB", &default_kv, NULL); if (result != FDB_NO_ERR) { return FALSE; } { struct fdb_blob blob; again: fdb_kv_get_blob(&kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); if (blob.saved.len > 0) { boot_count++; result = fdb_kv_set_blob(&kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); if (result != FDB_NO_ERR) { return FALSE; } return TRUE; } else { boot_count++; result = fdb_kv_set_blob(&kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); if (result != FDB_NO_ERR) { return FALSE; } goto again; } } } static fdb_time_t get_time(void) { /* Using the counts instead of timestamp. * Please change this function to return RTC time. */ return (int32_t)sys_millis(); } struct env_status { int temp; int humi; }; static bool ts_query_cb(fdb_tsl_t tsl, void *arg) { struct fdb_blob blob; struct env_status status; fdb_tsdb_t db = arg; fdb_blob_read((fdb_db_t)db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); return false; } static bool ts_query_by_time_cb(fdb_tsl_t tsl, void *arg) { struct fdb_blob blob; struct env_status status; fdb_tsdb_t db = arg; fdb_blob_read((fdb_db_t)db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); return false; } static BOOL test_flashdb_ts(void) { static fdb_err_t result; struct fdb_blob blob; /* TSDB object */ static struct fdb_tsdb tsdb = {0}; /* set the lock and unlock function if you want */ // fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)fdb_lock); // fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)fdb_unlock); /* Time series database initialization * * &tsdb: database object * "log": database name * "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. * Please change to YOUR partition name. * get_time: The get current timestamp function. * 128: maximum length of each log * NULL: The user data if you need, now is empty. */ result = fdb_tsdb_init(&tsdb, "log", "TSDB", get_time, 128, NULL); if (result != FDB_NO_ERR) { return FALSE; } { struct env_status status; status.temp = 37; status.humi = 85; result = fdb_tsl_append(&tsdb, fdb_blob_make(&blob, &status, sizeof(status))); status.temp = 38; status.humi = 90; result = fdb_tsl_append(&tsdb, fdb_blob_make(&blob, &status, sizeof(status))); { /* QUERY the TSDB */ /* query all TSL in TSDB by iterator */ fdb_tsl_iter(&tsdb, ts_query_cb, &tsdb); } { /* QUERY the TSDB by time */ /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */ struct tm tm_from = {.tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0}; struct tm tm_to = {.tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0}; time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to); static size_t count; /* query all TSL in TSDB by time */ fdb_tsl_iter_by_time(&tsdb, from_time, to_time, ts_query_by_time_cb, &tsdb); /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */ count = fdb_tsl_query_count(&tsdb, from_time, to_time, FDB_TSL_WRITE); count = count; __NOP(); } } return TRUE; } BOOL test_flashdb(void) { if (test_flashdb_kv() == FALSE) { return FALSE; } if (test_flashdb_ts() == FALSE) { return FALSE; } return TRUE; // 返回TRUE } /** * @brief I2C EEPROM写入测试 * @return {*} */ BOOL test_eeprom_fm24_write(void) { return TRUE; // 返回TRUE } /** * @brief I2C EEPROM读取测试 * @return {*} */ BOOL test_eeprom_fm24_read(void) { return TRUE; // 返回TRUE } /** * @brief 这是一个用于测试外部EEPROM的函数,它接受三个参数: * @param {uint16_t} addr - 要写入的地址。 * @param {uint8_t *} buf - 要写入的数据缓冲区。 * @param {uint16_t} len - 要写入的数据长度。 * @return {*} * @retval TRUE - 如果写入和读回的数据相同,则返回TRUE。 * @retval FALSE - 如果写入和读回的数据不同,则返回FALSE。 */ BOOL test_eeprom_fm24(void) { return TRUE; // 如果所有数据都相同,返回TRUE } /** * @brief 用于检测LCD是否正常工作,并初始化LCD * @return {*} */ BOOL test_lcd(void) { if (HW_VER >= 003) // 如果版本号大于等于003,表示使用了V3版本 { // 检测到LCD低电平才初始化LCD,V3版本该引脚被使用 if (1 == LCD_DETECT()) { return FALSE; // 如果检测到LCD低电平,说明LCD正常工作,返回FALSE } } gui_full(); // 填充屏幕 gui_open(); // 打开屏幕 return TRUE; // LCD正常工作,返回TRUE } BOOL test_led(void) { uint8_t i = 0; uint8_t ret = 0; for (i = 0; i < LEDS_MAX; i++) { leds_on((leds_e)i); delay_tick(10); // 等待10个时钟周期 switch (i) { case LEDS_RED: ret = GPIO_READ(LED1_GPIO_Port, LED1_Pin); break; case LEDS_GREEN: ret = GPIO_READ(LED2_GPIO_Port, LED2_Pin); break; case LEDS_YELLOW: ret = GPIO_READ(LED3_GPIO_Port, LED3_Pin); break; case LEDS_ORANGE: ret = GPIO_READ(LED4_GPIO_Port, LED4_Pin); break; case LEDS_BLUE: ret = GPIO_READ(LED5_GPIO_Port, LED5_Pin); break; default: break; } if (ret != 0) { return FALSE; // 如果读取到的状态不是1,表示设置失败 } } for (i = 0; i < LEDS_MAX; i++) { switch (i) { case LEDS_RED: GPIO_SET(LED1_GPIO_Port, LED1_Pin); delay_tick(10); ret = GPIO_READ(LED1_GPIO_Port, LED1_Pin); break; case LEDS_GREEN: GPIO_SET(LED2_GPIO_Port, LED2_Pin); delay_tick(10); ret = GPIO_READ(LED2_GPIO_Port, LED2_Pin); break; case LEDS_YELLOW: GPIO_SET(LED3_GPIO_Port, LED3_Pin); delay_tick(10); ret = GPIO_READ(LED3_GPIO_Port, LED3_Pin); break; case LEDS_ORANGE: GPIO_SET(LED4_GPIO_Port, LED4_Pin); delay_tick(10); ret = GPIO_READ(LED4_GPIO_Port, LED4_Pin); break; case LEDS_BLUE: GPIO_SET(LED5_GPIO_Port, LED5_Pin); delay_tick(10); ret = GPIO_READ(LED5_GPIO_Port, LED5_Pin); break; default: break; } if (ret != 1) { return FALSE; // 如果读取到的状态不是1,表示设置失败 } } return TRUE; } /** * @brief 这是一个用于测试PWM输出的函数。 * @brief * @param {uint16_t} dac: 目标输出电流值 * @return {*}: 返回一个布尔值,表示函数执行结果 */ BOOL test_pwm_out(float32 current) { pwm_duty2current(current); return TRUE; } /** * @brief 测试按键,通过HART测试 * @return {*} */ BOOL TEST_BUTTON(void) { return TRUE; } /** * @brief 用于测试hart cache,向cache添加数据 * @return {*} * @note */ hart_cache_t cache_node_test; void hart_cache_test(void) { cache_node_test.uuid = 1; cache_node_test.hart_cache_time = 5000 + sys_millis(); hart_cache_add(cache_node_test); } /************************** 开发板驱动自测 **************************/ float32 temp_f; float32 dcdc; float32 loop_cur; uint16_t adc[10]; rtc_date s_date = {0x23, 0x08, 0x07, 0x01, 0x16, 0x30, 0x30}; rtc_date r_date; uint8_t e_data[5] = {0xD5, 0xC8, 0x00, 0x01, 0x03}; static void test_init(void) { driver_init(); } static void test_dinit(void) { driver_dinit(); } void bsp_test(void) { test_init(); // 测试DCDC DBG_ASSERT(test_dcdc(&dcdc) == TRUE __DBG_LINE); // 测试回路电流 DBG_ASSERT(test_loop_current(&loop_cur) == TRUE __DBG_LINE); // 测试温度 DBG_ASSERT(test_temperature(&temp_f) == TRUE __DBG_LINE); // 测试IP大电流输出使能 DBG_ASSERT(test_gpio(IP_H_PWR_GPIO_Port, IP_H_PWR_Pin) == TRUE __DBG_LINE); // // 测试IP输出控制 // DBG_ASSERT(test_ip_putput_dac(3000) == TRUE __DBG_LINE); // 测试PWM输出 test_pwm_out(20); // 测试RTC DBG_ASSERT(test_rtc(RTC_WRITE, &s_date) == TRUE __DBG_LINE); // 设置时间 delay_ms(2000); DBG_ASSERT(test_rtc(RTC_READ, &r_date) == TRUE __DBG_LINE); // 读取时间 // // 测试SPI EEPROM // DBG_ASSERT(test_eeprom_m95(M95_1, (_M95_SIZE - 32), e_data, 10) == TRUE __DBG_LINE); // DBG_ASSERT(test_eeprom_m95(M95_2, (_M95_SIZE - 32), e_data, 10) == TRUE __DBG_LINE); // 测试flashdb 不要开放 // DBG_ASSERT(test_flashdb() == TRUE __DBG_LINE); // // 测试I2C EEPROM // DBG_ASSERT(test_eeprom_fm24() == TRUE __DBG_LINE); // DBG_ASSERT(test_eeprom_lc02b((256 - 32), e_data, 10) == TRUE __DBG_LINE); // // 测试LED // DBG_ASSERT(test_led() == TRUE __DBG_LINE); // 测试LCD,需要人工观察现象 // DBG_ASSERT(test_lcd() == TRUE __DBG_LINE); // delay_ms(2000); // 测试各个ADC通道 DBG_ASSERT(test_adc(ADC_PSB_CHANNEL, &adc[1]) == TRUE __DBG_LINE); // 位置反馈 DBG_ASSERT(test_adc(ADC_IPSB_CHANNEL, &adc[2]) == TRUE __DBG_LINE); // 小回路 DBG_ASSERT(test_adc(ADC_BP_CHANNEL, &adc[3]) == TRUE __DBG_LINE); // 气源压力 DBG_ASSERT(test_adc(ADC_BPA_CHANNEL, &adc[4]) == TRUE __DBG_LINE); // A路压力 DBG_ASSERT(test_adc(ADC_BPB_CHANNEL, &adc[5]) == TRUE __DBG_LINE); // B路压力 test_dinit(); } // NOTE: 以下函数为自测函数,不要开放,因为由fal_execution维护 // BOOL selftest_m95_1(void) // { // BOOL ret; // ret = test_eeprom_m95(M95_1, (_M95_SIZE - 32), e_data, 10); // return ret; // } // BOOL selftest_m95_2(void) // { // BOOL ret; // ret = test_eeprom_m95(M95_2, (_M95_SIZE - 32), e_data, 10); // return ret; // } // BOOL selftest_fm24(void) // { // BOOL ret; // ret = test_eeprom_fm24(); // return ret; // } BOOL selftest_lc02(void) { BOOL ret; ret = test_eeprom_lc02b((256 - 32), e_data, 10); return ret; } /** * @brief 功耗测试 * @return {*} * @note */ static void power_test_open_pressure(void) { BP_A_POWER_ON(); BP_B_POWER_ON(); // BP_S_POWER_ON(); } static void power_test_open_led(void) { leds_on(LEDS_GREEN); // leds_on(LEDS_RED); // leds_on(LEDS_ORANGE); // leds_on(LEDS_BLUE); // leds_on(LEDS_YELLOW); } static void power_test_output_swo(void) { SWO1_OPEN(); // SWO2_OPEN(); } static void power_test_output_pwm(uint16_t current) { test_pwm_out(current); } void power_test(void) { // 初始化 test_init(); // 使能压力传感器 power_test_open_pressure(); // 使能LED power_test_open_led(); // 使能SWO power_test_output_swo(); // 使能PWM power_test_output_pwm(20); }