jiujiu · 2022年12月31日 · 河北

【GD32F427开发板试用】+ 项目移植+体征参数测试

一、 拆箱及硬件

1.jpg
熟悉特性和时序:
先熟悉一下这个器件的一些特性和时序。
22.png
数据格式
3.png
时序图
从时序图来看,只需要先把PC9拉高然后拉低大于18ms然后再拉高,激活模块,就可以等待模块相应低电平,在等待响应高电平,就可以接收40bit数据。然后再拉高PC9引脚就可以读取到湿度和温度数据。
学习一个新的东西的最快方式是看他的使用示例,并且驱动起来

没有USB转串口的模块,这意味着,想要使用串口调试,必须外接一个转串口工具。用以前项目开发用过的CH340,该芯片有5个引脚,2个VCC(5V/3.3V),1个GND,1个RXD,1个TXD。

它和F427的接线方法如下:

CH340 F427  
VCC3.3 ------> VCC3.3  
GND ------> GND  
TXD ------> RXD(PB7)  
RXD -------> TXD(PB6)

CH340一端接F427,一端通过USB口接电脑,同时F427也通过USB供电。如图所示:

二、 环境配置

(1)Keil安装
建议安装高版本的,5.3以上的,我用的5.37的。需要安装包的可以加我QQ 984719097
(2)安装芯片pack包
pack包可以从https://aijishu.com/a/1060000000356925下载,选第8个选项 GD32F4xx AddOn中的 GigaDevice.GD32F4xx \_DFP.3.0.0.pack ,如下图所示:
4.png
下载到本地直接安装即可。
(3)demo程序下载
首先将demo下载下来,然后按此路径打开:\GD32F4xx\_Demo\_Suites\_V2.6.1\GD32427V\_START\_Demo\_Suites\Projects 可以看到F427V\_START有6个demo,先将第一个demo下载到板子上。
下载前注意事项:
demo中的代码是keil4编写的,因此,想要让demo能下载,建议参考【GD32F427开发板试用】+demo的正确打开方式(一),转换成keil5,再配置下GDlink,即可点击下载。
(4)创建project
demo程序下载后,验证了开发板的基础功能,接下来就是创建自己的project。

首先,keil5点创建项目,再选择芯片:
5.png

接着引入文件,第一次创建project,使用官方的固件库和demo内的文件完成project的搭建:

固件库需要用到的目录有:
6.png

接着开始创建,参照如下顺序:
7.png
首先点击1所在的图标,然后点击2修改project name,接着点击3按钮,新增代码目录。将代码设计成4个目录:

  • APP(应用目录)
  • BSP(板级别文件的目录)
  • FMW\_CORE(内核相关文件目录)
  • FMW\_PERI(外设相关文件目录)

创建完代码目录,向每个目录中添加对应的文件。

此项目参照固件库中的GPIO的例程,如下所示:
8.png
例程中的文件包括了应用文件和板级别文件,应用文件main.c添加到APP目录,其余板级文件添加到BSP。

除了这些文件外,还要去utilities中添加F427的头文件,但此目录下,只有f450的文件,如下所示:
9.png

因此,可以去demo的utilities中添加F427的文件,如下图所示:
10.png

接着添加 FMW\_CORE内核相关文件目录,在固件库Firmware\CMSIS\GD\GD32F4xx\添加内核文件和.s汇编启动文件,在Firmware\GD32F4xx\_standard\_peripheral添加此project涉及的外设,如uart,gpio,exit等。如下图所示:
11.png
创建完project:
12.png
接着开始编译,报了一些错误,找不到头文件,因此需要对project做一些配置:在C/C++处设置头文件路径,如下所示:
13.png
将BSP,内核,外设的头文件路径都包含进来。

Keil5目前使用的Compiler6,要用编译器5来编译。因此,需要安装编译器5,编译器5的安装文件,可以去嵌入式小学生公众号,回复ARMCC即可获得,下载后直接安装,在project配置中就会出现2个编译器,如图所示:
14.png

用编译器5,此project正常编过。

三、 移植及体征参数测试

以前项目做过体征参数测试,这次简单移植,并做体温、心率、心电、舒张压收缩压及血氧饱和度六中数据,分别对应对应了四种传感器设备,即温度传感器、心电模块、气压传感器、SpO²模块。

原系统基于STM32
QQ截图20221231172652.jpg
使用传感器型号
传感器使用

| 功能监测 | 型号 |

| 血氧、心率 | MAX86174A |

| 心电 | ECG,AD8232 |

| 呼吸 | MAX30001 |
| 体温 | 10k NTC热敏电阻 |
| 血压 | XGZP6847 |
| 加速度 | ADXL345BCCZ-RL7 |

数据流程

122.png
需要多路ADC偶同时采样传输,开启浮点运算,进行ADC1采样四路传感器,使用DMA1将数据搬运至内存,并串口打印显示。
此处参考Hello\_eQN7e7的配置分享。https://aijishu.com/a/1060000000375869

  1. 使用keil进行浮点运算开启 如图所示开启即可:
    133.png
  2. 检查文件中关于FPU寄存器配置是否高亮 如图所示即可:
    144.png
    硬件配置:
    15.png
  3. ADC硬件查询:通过相应数据手册查询可得PC1 PC2 PC3 PC4分别对应ADC的通道11 通道12 通道13 通道14,基于此信息将此三个引脚作为模拟引脚进行采样数据,数据手册信息如下所示:
    16.png
  4. DMA硬件查询:通过GDF4的用户手册得知DMA的通道2 通道3对应ADC1 这里选择通道3作为ADC1传输数据的通道 外设请求通道信息如下所示:
    ADC软件编写
    17.png
  5. ADC四路引脚初始化:
    18.png
  6. ADC各参数初始化:
    19.png
  7. 总体初始化函数:
    20.png
    DMA软件编写
  8. DMA基本参数初始化:
    111.png
  9. DMA通道参数初始化:
    222.png
  10. DMA数据简单处理打印:
    333.png
    打印采集数据444.png

下面采集各传感器的ADC值

/ _阅读采集自心率传感器的ADC值_ /  
static void Ps\_ReadSampleValueFromAdc(void)  
{  

    rt_uint32_t value;
    
    /* 读取采样值 */
    value = Adc_ChSample(ADC_CHANNEL_1);
    
    /* 转换采集到的ADC值的精度为10bits */
    Signal = value>>2;

}  

其他三种传感器采集类似。
多种传感器信息融合:
借助paddlepaddle环境移植信息已训练好的融合算法模型进行预测:

import argparse  
import os  
  
import paddle  
  
from paddleseg.cvlibs import manager, Config  
from paddleseg.utils import get\_sys\_env, logger, config\_check  
from paddleseg.core import predict  
  
def parse\_args():  

    parser = argparse.ArgumentParser(description='Model prediction')
    
    # params of prediction
    parser.add_argument(
        "--config_1", dest="cfg_1", help="The config file.", default=None, type=str)
    parser.add_argument(
        "--config_2", dest="cfg_2", help="The config file.", default=None, type=str)
    
    parser.add_argument(
        '--model_path_1',
        dest='model_path_1',
        help='The path of model 1 for evaluation',
        type=str,
        default=None)
    parser.add_argument(
        '--model_path_2',
        dest='model_path_2',
        help='The path of model 2 for evaluation',
        type=str,
        default=None)
    
    parser.add_argument(
        '--image_path',
        dest='image_path',
        help=
        'The path of image, it can be a file or a directory including images',
        type=str,
        default=None)
    parser.add_argument(
        '--save_dir',
        dest='save_dir',
        help='The directory for saving the predicted results',
        type=str,
        default='./output/result')
    
    # augment for prediction
    parser.add_argument(
        '--aug_pred',
        dest='aug_pred',
        help='Whether to use mulit-scales and flip augment for prediction',
        action='store_true')
    parser.add_argument(
        '--scales',
        dest='scales',
        nargs='+',
        help='Scales for augment',
        type=float,
        default=1.0)
    parser.add_argument(
        '--flip_horizontal',
        dest='flip_horizontal',
        help='Whether to use flip horizontally augment',
        action='store_true')
    parser.add_argument(
        '--flip_vertical',
        dest='flip_vertical',
        help='Whether to use flip vertically augment',
        action='store_true')
    
    # sliding window prediction
    parser.add_argument(
        '--is_slide',
        dest='is_slide',
        help='Whether to prediction by sliding window',
        action='store_true')
    parser.add_argument(
        '--crop_size',
        dest='crop_size',
        nargs=2,
        help=
        'The crop size of sliding window, the first is width and the second is height.',
        type=int,
        default=None)
    parser.add_argument(
        '--stride',
        dest='stride',
        nargs=2,
        help=
        'The stride of sliding window, the first is width and the second is height.',
        type=int,
        default=None)
    
    return parser.parse_args()

def get\_image\_list(image\_path):  

    """Get image list"""
    valid_suffix = [
        '.JPEG', '.jpeg', '.JPG', '.jpg', '.BMP', '.bmp', '.PNG', '.png'
    ]
    image_list = []
    image_dir = None
    if os.path.isfile(image_path):
        if os.path.splitext(image_path)[-1] in valid_suffix:
            image_list.append(image_path)
    elif os.path.isdir(image_path):
        image_dir = image_path
        for root, dirs, files in os.walk(image_path):
            for f in files:
                if '.ipynb_checkpoints' in root:
                    continue
                if os.path.splitext(f)[-1] in valid_suffix:
                    image_list.append(os.path.join(root, f))
    else:
        raise FileNotFoundError(
            '`--image_path` is not found. it should be an image file or a directory including images'
        )
    
    if len(image_list) == 0:
        raise RuntimeError('There are not image file in `--image_path`')
    
    return image_list, image_dir

def main(args):  

    env_info = get_sys_env()
    place = 'gpu' if env_info['Paddle compiled with cuda'] and env_info[
        'GPUs used'] else 'cpu'
    
    paddle.set_device(place)
    if not args.cfg_1:
        raise RuntimeError('No configuration file specified.')
    if not args.cfg_2:
        raise RuntimeError('No configuration file specified.')
    
    cfg_1 = Config(args.cfg_1)
    cfg_2 = Config(args.cfg_2)
    
    val_dataset = cfg_1.val_dataset
    if not val_dataset:
        raise RuntimeError(
            'The verification dataset is not specified in the configuration file.'
        )
    
    msg = '\n---------------Config Information---------------\n'
    msg += str(cfg_1)
    msg += str(cfg_2)
    msg += '------------------------------------------------'
    logger.info(msg)
    
    model_1 = cfg_1.model
    transforms = val_dataset.transforms
    image_list, image_dir = get_image_list(args.image_path)
    logger.info('Number of predict images = {}'.format(len(image_list)))
    
    model_2 = cfg_2.model
    transforms = val_dataset.transforms
    image_list, image_dir = get_image_list(args.image_path)
    logger.info('Number of predict images = {}'.format(len(image_list)))
    
    config_check(cfg_1, val_dataset=val_dataset)
    
    predict(
        model_1,
        model_2,
        model_path_1=args.model_path_1,
        model_path_2=args.model_path_2,
    
        transforms=transforms,
        image_list=image_list,
        image_dir=image_dir,
        save_dir=args.save_dir,
        aug_pred=args.aug_pred,
        scales=args.scales,
        flip_horizontal=args.flip_horizontal,
        flip_vertical=args.flip_vertical,
        is_slide=args.is_slide,
        crop_size=args.crop_size,
        stride=args.stride,
    )

if **name** == '\_\_main\_\_':  

    args = parse_args()

main(args)  

显示:

//在指定位置显示一个字符,包括部分字符  
//x:0~127  
//y:0~63  
//mode:0,反白显示;1,正常显示  
//size:选择字体 16/12  
void OLED\_ShowChar(uint8\_t x, uint8\_t y, uint8\_t chr)  
{  

    uint8_t c = 0, i = 0;    
    c = chr - ' ';//得到偏移后的值            
    if(x > Max_Column - 1)
    {
        x = 0;
        y += 2;
    }
    if(SIZE == 16)
    {
        OLED_Set_Pos(x, y);    
        for(i = 0; i < 8; i ++)
        {
            OLED_WR_Byte(F8X16[c * 16 + i], OLED_DATA);
        }
        OLED_Set_Pos(x, y + 1);
        for(i = 0; i < 8; i ++)
        {
            OLED_WR_Byte(F8X16[c * 16 + i + 8], OLED_DATA);
        }
    }
    else 
    {    
        OLED_Set_Pos(x, y + 1);
        for(i = 0; i < 6; i ++)
        {
            OLED_WR_Byte(F6x8[c][i],OLED_DATA);
        }
    }

}  
//m^n函数  
uint32\_t oled\_pow(uint8\_t m,uint8\_t n)  
{  

    uint32_t result = 1;     
    while(n --)
    {
        result *= m;
    }    
    return result;

}  
  
//显示2个数字  
//x,y :起点坐标  
//len :数字的位数  
//size:字体大小  
//mode:模式 0,填充模式;1,叠加模式  
//num:数值(0~4294967295);  
void OLED\_ShowNum(uint8\_t x, uint8\_t y, uint32\_t num, uint8\_t len, uint8\_t size)  
{  

    uint8_t t,temp;
    uint8_t enshow = 0;                           
    for(t = 0; t < len; t ++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;
        if(enshow == 0 && t < (len - 1))
        {
            if(temp == 0)
            {
                OLED_ShowChar(x + (size / 2) * t, y, ' ');
                continue;
            }else enshow = 1; 
        }
         OLED_ShowChar(x + (size / 2) * t, y, temp + '0'); 
    }

}  
  
//显示一个字符号串  
void OLED\_ShowString(uint8\_t x, uint8\_t y, uint8\_t \*chr)  
{  

    uint8_t j = 0;
    while (chr[j] != '\0')
    {        
        OLED_ShowChar(x, y, chr[j]);
        x += 8;
        if(x > 120)
        {
            x = 0; 
            y += 2;
        }
        j++;
    }

}  
  
////显示汉字  
void OLED\_ShowCHinese(uint8\_t x, uint8\_t y, uint8\_t no)  
{  

    uint8_t t, adder = 0;
    OLED_Set_Pos(x, y);    
    for(t = 0; t < 16; t ++)
    {
        OLED_WR_Byte(Hzk[2 * no][t], OLED_DATA);
        adder += 1;
    }    
    OLED_Set_Pos(x, y + 1);    
    for(t = 0; t < 16; t ++)
    {    
        OLED_WR_Byte(Hzk[2 * no + 1][t], OLED_DATA);
        adder += 1;
    }

}  

运算结果
1111.jpg
测量页面
2222.jpg

在交互界面上的显示:
3333.png

四、 总结

感谢极术社区提供的开发板,让我亲身体验国产芯片资源的优势。
祝极术社区越办越好,也祝国产芯片、国家科技和创新节节攀升!

五、 参考链接

  1. 【极术社区】GD32F47x/42x系列ARM Cortex-M4高性能MCU资料汇总
  2. 【Keil官网】 MDK v4 Legacy Support (keil.com)
  3. 【GD官网】GD32F427xx数据手册: https://www.gigadevice.com.cn...
  4. 【GD官网】GD32F427xx用户手册: https://www.gigadevice.com.cn...
  5. 【CSDN】STM32 arm-none-eabi-gcc 交叉编译重定向printf: https://blog.csdn.net/weixin\_...
  6. 【CSDN】编译报错—undefined reference to \\_sbrk: https://blog.csdn.net/jackcsd...
  7. 【腾讯云社区】ARM探索之旅03 | 如何使用 ARM FPU 加速浮点计算:https://cloud.tencent.com/dev...
推荐阅读
关注数
10693
内容数
187
中国高性能通用微控制器领域的领跑者兆易创新GD系列芯片技术专栏。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息