本帖最后由 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 一切准备继续后,我们可以编译下载看看执行效果: 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. 测试效果 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.实测效果 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.实测演示 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的亮度试验达到预期目的。 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); 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); 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); 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);
|