之前一直忙于工作冷落了MM32的开发板,这几天闲下来,写了些代码测试板载外设是否能正常工作,也体验了一番MM32库函数。接下来我分享一下这几天我做的东西和我的想法。
一、MM32主控芯片使用感受
这次申请的是MM32W373系列的板子跟STM32很类似,开发方式也一样,如果使用过STM32,MM32系统的芯片就可以简单上手。我从数据手册对比了一下MM32L3系列和STM32F103系列的芯片,发现它们的总线架构很类似,很多外设存储器映射都一样。
(1)MM32L3部分存储器映射
(2)STM32F10x部分存储器映射
这两种芯片还是很有差异的,MM32W373系列芯片内部集成有2.4Ghz的射频芯片。两种芯片有些外设寄存器一样,有些是有差异的。GPIO的外设寄存器是一样的,但是UART外设寄存器就不一样。MM32L3系列的库函数和STMF10x系列库函数能不能共用呢?有些可以,比如GPIO口,有些就不行比如UART。二、测试程序简单说明
官网上没有MM32W3系列芯片的例程,但是听说MM32W3和MM32L3是一样的内核,所以使用了MM32L3的例程来做测试。这是的小测试主要有以下几个功能:
①按下按键,对应的LED灯反转亮灭
②通过串口助手,向开发板发送命令BEEPON、BEEPOFF控制蜂鸣器
③输入命令GETADC,获取ADC检测的值
④输入命令TESTEEP,测试板载的EEPROM。
1.LED测试简单说明
从MM32L3移植过来的例程,需要把LED引脚定义改成MB-021开发板上的引脚。MB-021上LED灯使用的引脚为GPIOA_15、GPIOC_10、GPIOC_11、GPIO12
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOC, &GPIO_InitStructure);
复制代码一样的,从MM32L3移植过来的程序,需要把按键引脚定义改了,需要注意的是K1接到PA0可以做唤醒功能,K1在外部做了上拉,K2、K3、K4做了下拉。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//PB1,K1(WK_UP) GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_10 | GPIO_Pin_11; //PB2,PB10,PB11,K2,K3,K4 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.2,10,11
复制代码开发板上的Uart输出口接的是芯片的UART2,所以程序中要使用的是UART。我使用了printf函数直接向串口输出信息,所以重定义fputc函数。
int fputc(int ch, FILE *f){ while((UART2->CSR & UART_IT_TXIEN) == 0); //循环发送,直到发送完毕 UART2->TDR = (ch & (uint16_t)0x00FF); return ch; }
复制代码void UART2_IRQHandler(void){ uint8_t i=0; if(UART_GetFlagStatus(UART2,UART_IT_RXIEN)==SET) { if(UART_ReceiveData(UART2)=='\n') { UART_Finish_FLAG=1; RecBuffer[sp]='\0'; sp=0; } else { if(sp==0) { for(i=0;i<REVBUFFSIZE;i++) { SendData[i]='\0'; RecBuffer[i]='\0'; } } RecBuffer[sp]=UART_ReceiveData(UART2); SendData[sp]=RecBuffer[sp]; sp++; } } if(UART_GetITStatus(UART2,UART_IT_RXIEN)!=RESET) { UART_ClearITPendingBit(UART2,UART_IT_RXIEN); } }
复制代码开发板的adc引脚接的是PA5引脚,初始化时使用配置为单次模式。每次需要读取时使用ADC_SoftwareStartConvCmd(ADC1, ENABLE)使能ADC转换,这里测量多次取平均值。
u16 ADC1_SingleChannel_Get(uint8_t ADC_Channel_x){ u16 puiADData; ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(!(ADC1->ADSTA & ADC_FLAG_EOC)); puiADData = ADC1->ADDATA & 0xfff; ADC_ClearFlag(ADC1, ADC_FLAG_EOC); /*ADCR寄存器的ADST位失能,软件转换结束*/ return puiADData; } u16 Get_Adc_Average(uint8_t ADC_Channel_x, uint8_t times) { u32 temp_val = 0; u8 t; u8 delay; for(t = 0; t < times; t++) { temp_val += ADC1_SingleChannel_Get(ADC_Channel_x); for(delay = 0; delay < 100; delay++); } return temp_val / times; }
复制代码蜂鸣器控制引脚接到了PA8引脚,这里使用的TIM的PWM模式控制蜂鸣器发声。当需要蜂鸣器发声时调用TIM_CtrlPWMOutputs(TIM1, ENABLE);关闭蜂鸣器时关闭TIM_CtrlPWMOutputs(TIM1, DISABLE);
void TIM1_PWM_Init(void){ GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_ClocksTypeDef RCC_Clocks; RCC_GetClocksFreq(&RCC_Clocks); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO, ENABLE); //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM3_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); TIM_TimeBaseStructure.TIM_Period = (1000 - 1); //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.SYSCLK_Frequency / 1000000 - 1);; //设置用来作为TIMx时钟频率除数的预分频值 不分频 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能 TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高 TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能 TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器 TIM_Cmd(TIM1, ENABLE); //使能TIM1 TIM_CtrlPWMOutputs(TIM1, DISABLE); }
复制代码主程序里先初始化硬件,然后循环执行按键和LED测试部分程序、检查是否收到数据,如果收到数据,根据收到的数据执行相应的命令。
int main(void){ u8 key_flag=0; u8 len; delay_init(); LED_Init(); KEY_Init(); uart_initwBaudRate(UART2,115200); TIM1_PWM_Init(); TIM_SetCompare1(TIM1, 200); I2C_Initializes(); ADC1_Init( ADC_Channel_5); Beep_init(); delay_ms(1000); printf("\n This is a MM32 Test Demo!!!\n"); printf("\n You can input the following command:\n"); printf("\n1.BEEPON\n"); printf("\n2.BEEPOFF\n"); printf("\n3.GETADC\n"); printf("\n4.TESTEEP\n"); while(1) { Led_Key_Test(); if(UART_Finish_FLAG) { len=strlen(RecBuffer)-1; if(!strncmp(RecBuffer,"BEEPON",len)) { Beep_On(); printf("BEEPON_OK \n"); } else if(!strncmp(RecBuffer,"BEEPOFF",len)) { Beep_Off(); printf("BEEPOFF_OK \n"); } else if(!strncmp(RecBuffer,"GETADC",len)) { Get_AdcValue(); printf("GETADC_OK \n"); } else if(!strncmp(RecBuffer,"TESTEEP",len)) { printf("TESTEEP_OK \n"); ee_Test(); } else { printf("Input error!!!\n"); printf("Please Input again!!!\n"); } UART_Finish_FLAG=0; } } }
复制代码这次因为时间有限,所以只是对板载硬件做了一个简单的测试。开发板上的FLASH一直没成功,不知道是什么原因,拨码开关也拨了还是不行,有时间检查Flash部分电路是不是坏了。代码源码放于https://gitee.com/Liyj_336/MM32_Test.git 有兴趣可以下载。
测试结果