[LKS32 硬件] 【LKS32MC081评测】 +GCC下外设驱动测试

[复制链接]
13|2
手机看帖
扫描二维码
随时随地手机跟帖
coslight|  楼主 | 2022-6-22 08:29 | 显示全部楼层 |阅读模式
本帖最后由 coslight 于 2022-6-22 08:38 编辑

#申请原创#
LKS32MC081评测+GCC下外设测试
基于开发环境搭建的平台,继续进行MC081芯片外设的评测。由于部分文件官方提供的是二进制格式的,在GCC环境下使用不了,所有ADC的测试没有使用官方的校准数据。
1、  硬件资源分配
LED灯的资源分配,系统中包含多个电源指示LED灯,这里仅列出控制器可控制的LED灯对应的资源。
序号
名称
对应引脚
说明
1
LED1
P0.6
CAN_RX
2
LED2
P0.7
CAN_TX
3
LED3
P0.3
-
UART的资源分配,底板的P4连接器对应UART0的通讯接口。
序号
名称
对应引脚
说明
1
UART0_TX
P1.0
-
2
UART0_RX
P0.15
-
       ADC的资源分配,底板上包含多路ADC采集通道,本次仅测试ADC的基本使用,但列出所有ADC的采集通道。电流输入为差分输入方式,其它为单端输入方式。
序号
ADC
通道
对应引脚
说明
1
UP(U相分支电流)
OPA2_IP,OPA2_IN
差分输入
2
VP(V相分支电流)
OPA1_IP,OPA1_IN
差分输入
3
WP(W相分支电流)
OPA0_IP,OPA0_IN
差分输入
4
PGND(干路电流)
OPA3_IP,OPA3_IN
差分输入
5
滑线变阻器(ADC_CH9
P0.5
单端输入
6
母线电压(ADC_CH12
P2.9
单端输入

       按键的资源分配,底板集成了2个按键,分别为START和STOP按键。

序号
名称
对应引脚
说明
1
START
P2.11
-
2
STOP
P2.12
-

       霍尔输入资源分配。
序号
通道
对应引脚
说明
1
HALL_W
P2.4
HALL_IN0
2
HALL_V
P2.5
HALL_IN1
3
HALL_U
P2.6
HALL_IN1

       反电动势输入资源分配。
序号
通道
对应引脚
说明
1
PW
P0.11
CMP0_IP1
2
PV
P0.12
CMP0_IP2
3
PU
P0.13
CMP0_IP3


       PWM驱动信号输出资源分配。
序号
通道
对应引脚
说明
1
W相(P:高侧,N:低侧)
P1.8,P1.9
MCPWM_CH2
2
V相(P:高侧,N:低侧)
P1.6,P1.7
MCPWM_CH1
3
U相(P:高侧,N:低侧)
P1.4,P1.5
MCPWM_CH0

使用特权

评论回复
coslight|  楼主 | 2022-6-22 08:33 | 显示全部楼层
本帖最后由 coslight 于 2022-6-22 08:36 编辑

续上贴:
2、  外设驱动测试
为了更好的完成外设驱动测试,这里移植了Letter-shell,通过shell下执行相关的测试命令,完成相关的测试过程。由于shell自身需要UART外设,所以,UART的测试过程和shell的移植测试过程合为一个。
1)      UART的测试(Letter-shell移植)
这里已经准备好了Letter-shell的源码,请各位大神自行获取源码。
移植过程主要包含两个关键点,一个是shell_Port的移植,另一个为在GCC环境下的脚本文件配置。
A.    串口驱动
为了更好的完成shell的数据收发,决定采用UART0的查询发送和中断接收方式。
前述表格已经表明UART0的关键分配关系,因此,GPIO的初始化为:
    //配置UART0_RXD  P0.15
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO0, &GPIO_InitStruct);
    //配置UART0_TXD  P1.0
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_Init(GPIO1, &GPIO_InitStruct);
    GPIO_PinAFConfig(GPIO0, GPIO_PinSource_15,AF4_UART);
    GPIO_PinAFConfig(GPIO1, GPIO_PinSource_0,AF4_UART);
UART0串口的配置,波特率38400,8N1:
       void UART_init(void)
{
    UART_InitTypeDef UART_InitStruct;
    UART_StructInit(&UART_InitStruct);
    UART_InitStruct.BaudRate = 38400;               /* 设置波特率38400 */
    UART_InitStruct.WordLength = UART_WORDLENGTH_8b;   /* 发送数据长度8位 */
    UART_InitStruct.StopBits = UART_STOPBITS_1b;        /* 停止位1位 */
    UART_InitStruct.FirstSend = UART_FIRSTSEND_LSB;     /* 先发送LSB */
    UART_InitStruct.ParityMode = UART_Parity_EVEN;     /* 无奇偶校验 */
    UART_InitStruct.IRQEna = UART_IRQEna_RcvOver;     /* 串口中断使能 */
    UART_Init(UART0, &UART_InitStruct);
    UART0_IF = 0xff;
}
B.    shell_Port的移植
由于我们采用中断方式接收串口数据,所以这里只需要移植一个程序,就是写函数:
       void userShellWrite(char data)
{
         UART0_SendByte(data);
}
/*****************  发送一个字节 **********************/
void UART0_SendByte(uint8_t ch)
{
  UART0->BUFF = ch;
   while(!(UART0->STT & 0x01));
}
UART0_SendByte函数和厂家驱动有所不同,增加了UART0发送完成状态检查。
串口接收中断中,增加shell接收的处理:
       void UART0_IRQHandler(void)
{
        u8 UART_Value = 0;
        if (UART0_IF & UART_IF_RcvOver) //接收完成中断
        {
            UART0_IF= UART_IF_RcvOver;
            UART_Value= UART0->BUFF;
            shellHandler(&shell, UART_Value &0xff);
        }
}
C.    GCC环境下,脚本文件配置
这一步比较关键,如果不能合理配置,将来无法自动增加我们自定义函数的引入。
在sections.ld脚本文件中,增加如下信息:
    .shell :
    {
      _shell_command_start = .;
      KEEP (*(shellCommand))
      _shell_command_end = .;
    } >FLASH AT>FLASH
       一切准备继续后,我们可以编译下载看看执行效果:
1.jpg
2)      LED灯的测试
基于前面的测试环境,我们增加LED外设驱动和操作接口。
A.    LED的端口初始化
IO端口初始化:
    //配置LED1:P0.6
    GPIO_StructInit(&GPIO_InitStruct);         //初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //GPIO输出模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO0, &GPIO_InitStruct);
    //配置LED2:P0.7
    GPIO_StructInit(&GPIO_InitStruct);         //初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //GPIO输出模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO0, &GPIO_InitStruct);
    //配置LED3: P0.3
    GPIO_StructInit(&GPIO_InitStruct);         //初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //GPIO输出模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO0, &GPIO_InitStruct);
       LED灯的基本函数
//LED12 3输出低
#defineLED1_OFF GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN)
#defineLED2_OFF GPIO_ResetBits(LED2_GPIO_PORT, LED2_GPIO_PIN)
#defineLED3_OFF GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN)
//LED12 3输出高
#defineLED1_ON GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN)
#defineLED2_ON GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN)
#defineLED3_ON GPIO_ResetBits(LED3_GPIO_PORT, LED3_GPIO_PIN)
//LED toggle
#defineLED1_TOGGLE() ((LED1_GPIO_PORT->PDO &LED1_GPIO_PIN)?(LED1_GPIO_PORT->PDO &=~LED1_GPIO_PIN):(LED1_GPIO_PORT->PDO |= LED1_GPIO_PIN))
#defineLED2_TOGGLE() ((LED2_GPIO_PORT->PDO &LED2_GPIO_PIN)?(LED2_GPIO_PORT->PDO &= ~LED2_GPIO_PIN):(LED2_GPIO_PORT->PDO|= LED2_GPIO_PIN))
#defineLED3_TOGGLE() ((LED3_GPIO_PORT->PDO &LED3_GPIO_PIN)?(LED3_GPIO_PORT->PDO &=~LED3_GPIO_PIN):(LED3_GPIO_PORT->PDO |= LED3_GPIO_PIN))
B.    操作函数
Shell下的LED灯控制函数:
//LED灯的控制函数
int led_ctl(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 3)
    {
     shellPrint(&shell,"led_ctl led1 on:!\n\r");
        return -1;
    }
    shellPrint(&shell,"argv[1]=%s argv[2]=%s\n\r",argv[1],argv[2]);
    if(strcmp(argv[1], "led1") == 0)
    {
     if(strcmp(argv[2], "on") == 0)
         LED1_ON;
     else
         LED1_OFF;
    }
    else if(strcmp(argv[1], "led2") == 0)
    {
     if(strcmp(argv[2], "on") == 0)
         LED2_ON;
     else
         LED2_OFF;
    }
    else if(strcmp(argv[1], "led3") == 0)
    {
     if(strcmp(argv[2], "on") == 0)
         LED3_ON;
     else
         LED3_OFF;
    }
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), led_ctl, led_ctl, led controlled1 led2 led3 on off);
C.    测试效果
2.jpg
3)      按键的测试
系统包含两个按键,分别定义为START和STOP两个按键。
A.按键初始化
    //配置按键 start:P2.11
    GPIO_StructInit(&GPIO_InitStruct);        //初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //GPIO输入模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO2, &GPIO_InitStruct);
    //配置按键 stop:P2.12
    GPIO_StructInit(&GPIO_InitStruct);        //初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //GPIO输入模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIO2, &GPIO_InitStruct);
B.Shell操作函数
检测单个按键,并显示,如果两个按键同时按下,退出当前检测任务。
//KEY按键检测函数
int keyscaning(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    int key;
    while(1)
    {
     key =GPIO_KEY_Scan(1);
     if(key == start_PRES)
         shellPrint(&shell,"start key pressed!\n\r");
     else if(key == stop_PRES)
         shellPrint(&shell,"stop key pressed!\n\r");
     else if(key == all_PRES)
     {
         shellPrint(&shell,"quit key scan!\n\r");
         break;
     }
    }
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), keyscaning, keyscaning, all keypress to quit);
C.实测效果
3.jpg
4)      ADC测试
学习AD的使用方法,通过滑线变阻器的采样,验证AD的使用。采用软件触发方式,对分压电阻值采样。
A.AD初始化
采用一段扫描中断方式, 数据左对齐,软件触发,1倍增益。
void ADC0_init(void)
{
    ADC_InitTypeDef ADC_InitStructure;
   ADC_StructInit(&ADC_InitStructure);                          //初始化结构体
    ADC_InitStructure.IE = ADC_EOS0_IRQ_EN ; //第一段扫描结束中断使能
    ADC_InitStructure.Align = ADC_LEFT_ALIGN; //ADC数据输出左对齐
    ADC_InitStructure.UTIMER_Trigger_En = DISABLE; //UTIMER_T0硬件触发ADC采样
    ADC_InitStructure.MCPWM_Trigger_En = DISABLE; //关闭MCPWM的硬件中断      ADC_InitStructure.FirSeg_Ch = ADC_2_TIMES_SAMPLE; //第一段采样的总通道数: 1~20
    ADC_InitStructure.Trigger_Cnt = 0;  /*单段触发模式下触发一次采样所需要的事件数:0~15 0表示需要一次触发,15表示需要16次*/
    ADC_InitStructure.Trigger_Mode = ADC_1SEG_TRG; /*ADC采样1段模式*/
    ADC_InitStructure.DAT0_TH_Type = DISABLE; //设置ADC比较上阈值中断使能
    ADC_Init(ADC0, &ADC_InitStructure);
    ADC0_IF = 0xff;                                   //清中断标志位
    ADC0_GAIN0 = 0x1;                                 //开启1增益
    ADC0_CHN0 = ADC_CHANNEL_9 | (ADC_CHANNEL_9<< 8); //采样通道通道1和2
}
B.Shell函数
//ADC测试函数
int adc_test(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    int key;
    int adcval;
    shellPrint(&shell,"adc testbegin......!\n\r");
    ADC0_SWT= 0x5AA5;
    for(key = 0; key < 0x1fff; key++);
    while(!(ADC0_IF & BIT0));
    ADC0_IF= BIT0;
    adcval= ADC0_DAT0 >> 4;
    shellPrint(&shell,"adc value =%d\n\r", adcval);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), adc_test, adc_test, adc test stopkey to quit);
C.实测演示
4.jpg
5)      通用定时器测试
系统中的通用定时器UTimer包含4个独立的Timer ,这里测试仅进行Timer2的比较输出方式,通过占空比完成LED3小灯亮度调节测试。
A.初始化
IO端口初始化
Timer2的通道0比较输出可以选择为LED3——P0.3。
    /* 配置UTimer2  TIM2_CH0:P0.3 */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_Init(GPIO0, &GPIO_InitStruct);
   GPIO_PinAFConfig(GPIO0,GPIO_PinSource_3,AF8_TIMER23); //P0.3复用为timer2的输出模式
UTimer2定时器初始化
使用Timer2的通道0的比较输出功能,脉冲频率1KHz,占空比可调方式。
void UTimer_init(void)
{
    TIM_TimerInitTypeDef TIM_InitStruct;
    TIM_TimerCmd(TIMER2,ENABLE); /* Timer0 模块使能 */
   TIM_TimerStrutInit(&TIM_InitStruct);                  /* Timer结构体初始化/
    TIM_InitStruct.Timer_CH0_WorkMode = TIMER_OPMode_CMP; /* 设置Timer CH0 为比较模式 */
    TIM_InitStruct.Timer_CH0_CapMode = TIMER_CapMode_None;/* 无捕获*/
    TIM_InitStruct.Timer_CH0Output = 0;                   /* 计数器回零时,比较模式输出极性控制 */
    TIM_InitStruct.Timer_CH1_WorkMode = TIMER_OPMode_CAP; /* 设置Timer CH1 为驳货模式 */
    TIM_InitStruct.Timer_CH1_CapMode = TIMER_CapMode_None;
    TIM_InitStruct.Timer_CH1Output = 0;                   /* 计数器回零时,比较模式输出极性控制 */
    TIM_InitStruct.Timer_TH = 48000;     /* 定时器计数门限初始值1000*/
    TIM_InitStruct.Timer_CMP0 = 100; /* 设置比较模式的CH0比较初始值24000 */
    TIM_InitStruct.Timer_CMP1 = 24000; /* 设置比较模式的CH1比较初始值24000 */
    TIM_InitStruct.Timer_Filter0 = 0;/* 设置捕捉模式或编码器模式下对应通道的数字滤波值 */
    TIM_InitStruct.Timer_Filter1 = 0; /* 设置捕捉模式或编码器模式下对应通道的数字滤波值 */
TIM_InitStruct.Timer_ClockDiv = TIM_Clk_Div2;/* 设置Timer模块时钟2分频系数 */
    TIM_InitStruct.Timer_IRQEna = 0; /* 开启Timer模块比较中断和过零中断
    TIM_TimerInit(TIMER2,&TIM_InitStruct);
}
B.Shell调节函数
通过shell中断调用函数,修改Timer2-CH0的占空比,来调节LED3的显示亮度。
//timer2测试函数
int timer2_test(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 1)
    {
     shellPrint(&shell,"timer2_test 100 (0-48000)!\n\r");
     return -1;
    }
    unsigned int cmp = atoi(argv[1]);
    shellPrint(&shell,"compare count = %u\n\r", cmp);
    if(cmp <= 48000)
    {
     TIMER2->CMPT0 = cmp;
    }
    else
     shellPrint(&shell,"compare counterror!\n\r");
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), timer2_test, timer2_test,timer2_test change led light);
C.实测显示效果
通过shell终端修改timer2-CH0的占空比,来达到调节LED3的亮度试验达到预期目的。
5.jpg
6)      DSP使用测试
系统提供了DSP计算协处理器,可以完成整型数除法,开方,三角函数计算等复杂且耗时的数**算,且速度很快。这里仅测试DSP的被动调用,就是协处理器工作模式,即通过DSP数学协处理器完成复杂数学计算测试。
A.除法运算测试
//dspdiv 测试函数
int dsp_div(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 2)
    {
     shellPrint(&shell,"dsp_div dividend divider!\n\r");
     return -1;
    }
    long dividend = atol(argv[1]);
    long divider = atol(argv[2]);
    shellPrint(&shell,"dividend = %lddivider = %ld\n\r", dividend, divider);
    SYS_ModuleClockCmd(SYS_Module_DSP,ENABLE); /* DSP时钟使能*/
    DSP_SC |= BIT1;                             //DSP 协处理器模式
    DSP_DID = dividend;
    DSP_DIS = divider;
    int quotient = DSP_QUO;
    int remainder = DSP_REM;
    shellPrint(&shell,"DSP Calculate finished! quo= %d, rem = %d\n\r", quotient, remainder);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), dsp_div, dsp_div, DSP Calculate-- div);
6.jpg
B.开方运算测试
//dspsqrt 测试函数
int dsp_sqrt(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 1)
    {
     shellPrint(&shell,"dsp_div dividend divider!\n\r");
     return -1;
    }
    unsigned long input = (unsigned long)atol(argv[1]);
    shellPrint(&shell,"input = %lu\n\r", input);
    SYS_ModuleClockCmd(SYS_Module_DSP,ENABLE); /* DSP时钟使能*/
    DSP_SC |= BIT1;                             //DSP 协处理器模式
    DSP_RAD = input;
    uint16_t sqrt_val = DSP_SQRT;
    SYS_ModuleClockCmd(SYS_Module_DSP,DISABLE); /* DSP时钟失能*/
    shellPrint(&shell,"DSP Calculate finished! sqrt= %u\n\r", sqrt_val);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), dsp_sqrt, dsp_sqrt, DSP Calculate-- sqrt);
7.jpg
C.正弦和余弦运算测试
//dspsin cos 测试函数
int dsp_sincos(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 1)
    {
     shellPrint(&shell,"dsp_div dividend divider!\n\r");
     return -1;
    }
    int input = atoi(argv[1]);
    int angle = (int)((float)input * 32768 / 180);
    shellPrint(&shell,"input = %lu\n\r", input);
    SYS_ModuleClockCmd(SYS_Module_DSP,ENABLE); /* DSP时钟使能*/
    DSP_SC |= BIT2;                             //sin与cos计算
    DSP_CORDIC_THETA = angle;                  //角度
    int hSin = DSP_CORDIC_SIN;
    int hCos = DSP_CORDIC_COS;
    SYS_ModuleClockCmd(SYS_Module_DSP,DISABLE); /* DSP时钟失能*/
    shellPrint(&shell,"DSP Calculate finished! sin= %0.3f cos = %0.3f\n\r", (float)hSin/32768, (float)hCos/32768);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), dsp_sincos, dsp_sincos, DSPCalculate -- sin & cos);
8.jpg
D. 反正切运算测试
//dsparctan 测试函数
int dsp_arctan(int argc, char *argv[])
{
    int res = 0; /* API resultcode */
    if(argc < 2)
    {
     shellPrint(&shell,"dsp_div dividend divider!\n\r");
     return -1;
    }
    int inputy = atoi(argv[1]);
    int inputx = atoi(argv[2]);
    shellPrint(&shell,"inputy = %d inputx= %d\n\r", inputy,inputx);
    SYS_ModuleClockCmd(SYS_Module_DSP,ENABLE); /* DSP时钟使能*/
    DSP_SC&= (~BIT2);                      /* 计算模式选择 Arctan */
    DSP_CORDIC_X= inputx;                  //x
    DSP_CORDIC_Y= inputy;                  //y
    int angle = DSP_CORDIC_ARCTAN;
    SYS_ModuleClockCmd(SYS_Module_DSP,DISABLE); /* DSP时钟失能*/
    shellPrint(&shell,"DSP Calculate finished! arctan= %3.1f\n\r", (float)angle * 180 / 32768);
    return res;
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), dsp_arctan, dsp_arctan, DSPCalculate -- angle);
9.jpg


使用特权

评论回复
coslight|  楼主 | 2022-6-22 08:36 | 显示全部楼层
本帖最后由 coslight 于 2022-6-22 08:37 编辑

续上贴:
3、  测试源码
  基于第一贴工程的测试代码: 驱动测试.rar (35.42 KB)

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则