本帖最后由 lyj336 于 2020-6-16 22:55 编辑

之前一直忙于工作冷落了MM32的开发板,这几天闲下来,写了些代码测试板载外设是否能正常工作,也体验了一番MM32库函数。接下来我分享一下这几天我做的东西和我的想法。
一、MM32主控芯片使用感受
这次申请的是MM32W373系列的板子跟STM32很类似,开发方式也一样,如果使用过STM32,MM32系统的芯片就可以简单上手。我从数据手册对比了一下MM32L3系列和STM32F103系列的芯片,发现它们的总线架构很类似,很多外设存储器映射都一样。
MM32存储器映射.png
                (1)MM32L3部分存储器映射
F10x系列存储器映射.png
                (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
  1.     GPIO_InitStructure.GPIO_Pin  =  GPIO_Pin_15;
  2.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  3.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  4.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  5.     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
  6.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  7.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  8.     GPIO_Init(GPIOC, &GPIO_InitStructure);
2.按键测试简单说明
一样的,从MM32L3移植过来的程序,需要把按键引脚定义改了,需要注意的是K1接到PA0可以做唤醒功能,K1在外部做了上拉,K2、K3、K4做了下拉。
  1.     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;//PB1,K1(WK_UP)
  2.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //设置成下拉输入
  3.                 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB1
  4.     GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_10 | GPIO_Pin_11; //PB2,PB10,PB11,K2,K3,K4
  5.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  6.     GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB.2,10,11
3.Uart测试简单说明
开发板上的Uart输出口接的是芯片的UART2,所以程序中要使用的是UART。我使用了printf函数直接向串口输出信息,所以重定义fputc函数。
  1. int fputc(int ch, FILE *f)
  2. {
  3.     while((UART2->CSR & UART_IT_TXIEN) == 0); //循环发送,直到发送完毕
  4.     UART2->TDR = (ch & (uint16_t)0x00FF);
  5.     return ch;
  6. }
串口使用了接收中断,并以接收到换行"\n"字符,为结束符,然后置位接收成功标志位,在主程序中做处理。
  1. void UART2_IRQHandler(void)
  2. {
  3.                 uint8_t i=0;
  4.                 if(UART_GetFlagStatus(UART2,UART_IT_RXIEN)==SET)
  5.                         {
  6.                                 if(UART_ReceiveData(UART2)=='\n')
  7.                                 {
  8.                                        
  9.                                                 UART_Finish_FLAG=1;
  10.                                                 RecBuffer[sp]='\0';
  11.                                                 sp=0;        
  12.                                 }
  13.                                 else
  14.                                 {
  15.                                                 if(sp==0)
  16.                                                 {
  17.                                                         for(i=0;i<REVBUFFSIZE;i++)
  18.                                                         {
  19.                                                         SendData[i]='\0';
  20.                                                         RecBuffer[i]='\0';
  21.                                                         }
  22.                                                 }
  23.                                                 RecBuffer[sp]=UART_ReceiveData(UART2);
  24.                                                 SendData[sp]=RecBuffer[sp];
  25.                                                 sp++;
  26.                                 }
  27.                         }
  28.                 if(UART_GetITStatus(UART2,UART_IT_RXIEN)!=RESET)
  29.                 {
  30.                                 UART_ClearITPendingBit(UART2,UART_IT_RXIEN);
  31.                 }
  32. }
4.adc测试简单说明
开发板的adc引脚接的是PA5引脚,初始化时使用配置为单次模式。每次需要读取时使用ADC_SoftwareStartConvCmd(ADC1, ENABLE)使能ADC转换,这里测量多次取平均值。
  1. u16 ADC1_SingleChannel_Get(uint8_t ADC_Channel_x)
  2. {
  3.     u16 puiADData;
  4.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  5.     while(!(ADC1->ADSTA & ADC_FLAG_EOC));
  6.     puiADData = ADC1->ADDATA & 0xfff;
  7.     ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
  8.     /*ADCR寄存器的ADST位失能,软件转换结束*/
  9.     return puiADData;
  10. }
  11. u16 Get_Adc_Average(uint8_t ADC_Channel_x, uint8_t times)
  12. {
  13.     u32 temp_val = 0;
  14.     u8 t;
  15.     u8 delay;
  16.     for(t = 0; t < times; t++)
  17.     {
  18.         temp_val += ADC1_SingleChannel_Get(ADC_Channel_x);
  19.         for(delay = 0; delay < 100; delay++);
  20.     }
  21.     return temp_val / times;
  22. }
5.蜂鸣器测试简单说明
蜂鸣器控制引脚接到了PA8引脚,这里使用的TIM的PWM模式控制蜂鸣器发声。当需要蜂鸣器发声时调用TIM_CtrlPWMOutputs(TIM1, ENABLE);关闭蜂鸣器时关闭TIM_CtrlPWMOutputs(TIM1, DISABLE);
  1. void TIM1_PWM_Init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;
  4.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  5.     TIM_OCInitTypeDef  TIM_OCInitStructure;
  6.                 RCC_ClocksTypeDef  RCC_Clocks;
  7.                 RCC_GetClocksFreq(&RCC_Clocks);
  8.                 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_AFIO, ENABLE);
  9.     //设置该引脚为复用输出功能,输出TIM1 CH1的PWM脉冲波形
  10.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM3_CH1
  11.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  12.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  13.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  14.     TIM_TimeBaseStructure.TIM_Period = (1000 - 1); //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         80K
  15.     TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.SYSCLK_Frequency / 1000000 - 1);; //设置用来作为TIMx时钟频率除数的预分频值  不分频
  16.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;; //设置时钟分割:TDTS = Tck_tim
  17.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  18.                 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  19.     TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  20.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  21.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  22.     TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
  23.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  24.     TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
  25.     TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能
  26.     TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
  27.     TIM_Cmd(TIM1, ENABLE);  //使能TIM1
  28.                 TIM_CtrlPWMOutputs(TIM1, DISABLE);
  29. }
6.主程序简单说明
主程序里先初始化硬件,然后循环执行按键和LED测试部分程序、检查是否收到数据,如果收到数据,根据收到的数据执行相应的命令。
  1. int main(void)
  2. {
  3.                 u8 key_flag=0;
  4.                 u8 len;
  5.     delay_init();                    
  6.     LED_Init();
  7.                 KEY_Init();
  8.                 uart_initwBaudRate(UART2,115200);
  9.     TIM1_PWM_Init();
  10.     TIM_SetCompare1(TIM1, 200);
  11.                 I2C_Initializes();
  12.                 ADC1_Init( ADC_Channel_5);
  13.                 Beep_init();
  14.                 delay_ms(1000);
  15.                 printf("\n  This is a MM32 Test Demo!!!\n");
  16.                 printf("\n        You can input the following command:\n");
  17.                 printf("\n1.BEEPON\n");
  18.                 printf("\n2.BEEPOFF\n");
  19.                 printf("\n3.GETADC\n");
  20.                 printf("\n4.TESTEEP\n");
  21.                
  22.     while(1)
  23.     {
  24.                         Led_Key_Test();
  25.                         if(UART_Finish_FLAG)
  26.                         {
  27.                                 len=strlen(RecBuffer)-1;
  28.                                 if(!strncmp(RecBuffer,"BEEPON",len))
  29.                                 {
  30.                                         Beep_On();
  31.                                         printf("BEEPON_OK \n");
  32.                                 }
  33.                                 else if(!strncmp(RecBuffer,"BEEPOFF",len))
  34.                                 {
  35.                                         Beep_Off();
  36.                                         printf("BEEPOFF_OK \n");
  37.                                 }
  38.                                 else if(!strncmp(RecBuffer,"GETADC",len))
  39.                                 {
  40.                                         Get_AdcValue();
  41.                                         printf("GETADC_OK \n");
  42.                                 }
  43.                                 else if(!strncmp(RecBuffer,"TESTEEP",len))
  44.                                 {
  45.                
  46.                                         printf("TESTEEP_OK \n");
  47.                                         ee_Test();
  48.                                 }
  49.                                 else
  50.                                 {
  51.                                         printf("Input error!!!\n");
  52.                                         printf("Please Input again!!!\n");
  53.                                 }
  54.                                 UART_Finish_FLAG=0;
  55.                         }
  56.                                 
  57.     }               
  58. }


这次因为时间有限,所以只是对板载硬件做了一个简单的测试。开发板上的FLASH一直没成功,不知道是什么原因,拨码开关也拨了还是不行,有时间检查Flash部分电路是不是坏了。代码源码放于https://gitee.com/Liyj_336/MM32_Test.git  有兴趣可以下载。
测试结果
MM32测试结果.png