【敏矽微ME32G070开发板免费体验】串口通信、WS2812和PWM 呼吸灯、ADC 读取
本文通过具体工程案例,展示了 ME32G070 芯片在串口通信、PWM输出、ADC读取等方面的测试情况。
系统时钟树
ME32G070 具有非常灵活的时钟控制系统。
系统上电复位后, ME32G070 会一直采用HSI时钟直到用户使用软件切换到其它的时钟源。
系统可以在没有外部晶振时以已知的频率执行引导程序。
SYSAHBCLKCTRL寄存器用于存储器及外设时钟供给控制。
窗口看门狗(WWDG)计数时钟可来自于系统时钟(main clock), 而独立看门狗IWDG只可能是LSI。
主时钟(main clock),HSI及看门狗振荡器时钟都可从CLKOUT管脚输出。
RTC计数时钟可以是来自LSI,也可以来自外部32K晶振(LSE)。
系统主时钟可以是内部晶振时钟(HSI),外部晶振(HSE),内部低速时钟(LSI),外部RTC(LSE)以及PLL输出时钟。主时钟可作为内核、外设和存储器的时钟源。
1 串口通信
ME32G070 提供 5 个带 16 字节 FIFO 缓存器的增强型 UART 外设:
UART0/UART1/UART2/UART3/UART4。
串行接口都支持红外传输(IrDA)协议功能。
时钟都受 SYSAHBCLKCTRL 寄存器控制。
根据原理图可知
Type-C 接口与 CH340N 串口通信芯片相连,
发送 TX 和接收 RX 引脚通过跳线帽分别与 MCU 的 PA2 (MCU_RX)和 PA3 (MCU_TX)相连。
因此,若要实现Type-C线连接单片机串口,需要用跳线帽短接 4-3 及 2-1,如下图所示
串口发送
循环发送 12345678 字符至串口
代码
#include "me32g070.h"
#include "me32g070_sys.h"
#include "me32g070_uart.h"
#include "me32g070_gpio.h"
#include "me32g070_ioconfig.h"
uint8_t data_index=18;
uint8_t txdata[18]={0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x0D,0x0A};
int main(void)
{
SystemInit();
PA2_INIT(PA2_UART1_TX);
PA3_INIT(PA3_UART1_RX);
UART_Open(UART1,115200,UART_NO_PARITY,UART_TRIGGER_LEVEL_1_BYTE);
NVIC_EnableIRQ(UART1_IRQn);
while(1)
{
if ((data_index==18)&&(PB->PIN_b.PIN14==0))
{
data_index=0;
UART_EnableInt(UART1, UART_TX_INT);
}
}
}
void UART1_IRQHandler(void)
{
while(UART1->LSR_b.THRE)
{
if(data_index<18)
{
UART1->THR=txdata[data_index++];
}else
{
UART_DisableInt(UART1);
break;
}
}
}
效果
发送整型数字
发送 9 位字符
需要注意的是,
**若调试过程出现报错,则降低通信速率至 **500kHz 以下。
2 呼吸灯
WS2812 与 PWM 实现呼吸灯
介绍
ME32G070 系列提供 59 个内部 GPIO 管脚,用户需要根据使用的产品封装来操作 IO 端口。主要的特点有:
- 数字管脚可以用软件定义为输入或输出
- 管脚读写可以被屏蔽
- 多个管脚的置位、清零位用一条指令实现
- 管脚的输出取反
- 每一个管脚可作为外部中断信号
- 可编程的中断触发条件及中断优先级
16 位定时器/计数器
ME32G070 系列内置 4 个基本功能的 16 位定时器/计数器。
定时器/计数器工作时钟是 PCLK 时钟。关闭 APBCLKEN 寄存器中定时器/计数器的时钟供给可节省系统功耗。
主要功能如下:
- 可预置分频的16位定时器/计数器
- 4 个 16 位匹配寄存器
- 可产生中断
- 停止定时器
- 对定时器复位
- PWM输出
- 实现数据移位输出功能
- BTIM0/BTIM1 的 PWM 可合成并载波输出到 IR0_OUT
- BTIM2/BTIM3 的 PWM 可合成并载波输出到 IR1_OUT
- 2 路互补 PWM 伴随死区插入
定时器支持匹配输出 MAT0/MAT0N,它可以预留输出 PWM 波形,其中 MAT0N 是 MAT0 的互补输出。
MR0 决定 PWM 波的占空比,MR1 决定 PWM 的周期长度。
PWM 单线数据通信
使用 PWM 来实现单线数据通信传输,在 LED 炫彩灯带有着广泛的应用。
定时器通过两组匹配寄存器和两个 32 位的乒乓寄存器 (PPBUF0/PPBUF1) 轻松实现数据传输而不占用过多CPU资源。
首先,MR0/MR1 控制的 PWM 输出代表0,ALTMR0/ALTMR1 控制的 PWM 输出代表 1。
当在 MAT0 线上要输出 0 时,定时器自动切换到 MR0/MR1 控制的 PWM 输出;而但当在 MAT0 线上要输出 1 时,定时器自动切换到ALTMR0/ALTMR1 控制的 PWM 输出。
当乒乓数据寄存器的数据全部输出后,PWM 输出保持为低电平。
硬件
板载 WS2812 模块,根据原理图
**一线制 WS2812B 由 **PB15 引脚控制
代码
#include "me32g070.h"
#include "me32g070_ioconfig.h"
#include "me32g070_gpio.h"
#include "me32g070_sys.h"
#include "me32g070_timer.h"
#define MAX_LIGHT_NUMBER 8
__align(4) uint8_t org_rgb[3]={0xFF,0x0,0x0};
__align(4) uint8_t rgb[MAX_LIGHT_NUMBER][3];
uint32_t * rgbptr;
uint32_t * rgbendptr;
uint8_t start_color=0;
uint32_t data_index=0;
int main(void)
{
SystemInit ();
PB15_INIT(PB15_BTIM0_MAT0);
PC12_INIT(PC12_BTIM0_MAT0);
BTIM_Init(BTIM0, 16000000);
BTIM_ConfigPWMDataSift(BTIM0, 20, 5, 20, 15);
NVIC_EnableIRQ(BTIM0_IRQn);
BTIM_Init(BTIM1, 10000);
BTIM_ConfigMatch1(BTIM1, 1000, TIM_MATCH_RESET_COUNTER|TIM_MATCH_TRIGGER_INT);
NVIC_EnableIRQ(BTIM1_IRQn);
TIM_START(BTIM1);
PB->DIR_b.DIR10 =0x1;
PC->DIR_b.DIR4 =0x1;
while(1)
{
SYS_Delay(0x4FFFF);
}
}
void BTIM0_IRQHandler(void)
{
rgbptr=BTIM_DataSiftInt(BTIM0, rgbptr, rgbendptr);
BTIM_ClearIntFlag(BTIM0);
}
void BTIM1_IRQHandler(void)
{
uint32_t i;
switch (start_color)
{
case 0:
org_rgb[0]=org_rgb[0]-51;
org_rgb[1]=org_rgb[1]+51;
if (org_rgb[1]==0xFF)
start_color=1;
break;
case 1:
org_rgb[1]=org_rgb[1]-51;
org_rgb[2]=org_rgb[2]+51;
if (org_rgb[2]==0xFF)
start_color=2;
break;
case 2:
org_rgb[2]=org_rgb[2]-51;
org_rgb[0]=org_rgb[0]+51;
if (org_rgb[0]==0xFF)
start_color=0;
break;
}
for (i=MAX_LIGHT_NUMBER-1;i>0;i--)
{
rgb[i][0]=rgb[i-1][0];
rgb[i][1]=rgb[i-1][1];
rgb[i][2]=rgb[i-1][2];
}
rgb[0][0]=org_rgb[0];
rgb[0][1]=org_rgb[1];
rgb[0][2]=org_rgb[2];
rgbptr=(uint32_t *)rgb;
rgbendptr=rgbptr+((MAX_LIGHT_NUMBER*3)>>2);
if ((MAX_LIGHT_NUMBER*3)&0x3)
rgbendptr++;
rgbptr=BTIM_StartDataSift(BTIM0, rgbptr, rgbendptr);
BTIM_ClearIntFlag(BTIM1);
}
PWM 呼吸灯
ME32G070 系列可提供独立的事件驱动的脉宽调制模块 PWM。
PWM 模块可配置成 4 对互补输出,8 个独立输出或互补和独立混合的 PWM 信号输出(如3对互补输出,2个独立输出)。同时支持 0~100% 占空比的边沿对齐和中心对齐模式。
PWM 模块使用 16 位计数器,它的精度在边沿对齐时是一个时钟周期,中心对齐时是 2 个时钟周期。时钟周期由PWM 时钟源 PWM_PCLK (系统时钟)、预分频寄存器以及模数值决定。
当 PWM 信号配置成互补输出时,PWM 具有自动死区插入功能。每一个 PWM 输出可被 PWM 发生器、系统定时器、ADC 转换结果、GPIO 输入以及软件控制。非对称的 PWM 还可以允许在每半个周期改变 PWM 占空比而不需要软件参与。
多路的故障信号输入,可以有效的对外部环境进行实时响应。
PWM 重载,输出翻转事件都可触发 A/D 转换,定时器工作,适用于各种复杂应用。
特点
• 独立的脉宽调制模块
• 工作在系统时钟(System Clock)
• 8路PWM信号输出
– 单路独立输出模式
– 互补输出模式
– 混合输出模式
• 互补输出功能
– 单独的死区上升沿/下降沿插入
– 单独的高低脉冲宽度软件补偿
– 中心对齐下的非对称PWM输出
– 单独的高低脉冲输出极性控制
• 边沿对齐PWM输出
• 16 位调制精度
• 半周期重载机制
• 完善的1到16的重载频率控制
• 独立、可软件控制的PWM输出
• 多路故障输入保护
• 输出极性控制
• PWM控制寄存器写保护功能
• 可配置的PWM互补输出信号源
– PWM信号发生器
– 外部GPIO 管脚
– 定时器
– ADC转换
代码
#include "me32g070.h"
#include "me32g070_ioconfig.h"
#include "me32g070_gpio.h"
#include "me32g070_sys.h"
#include "me32g070_timer.h"
static float min(float a, float b, float c)
{
float m;
m = a < b ? a : b;
return (m < c ? m : c);
}
static float max(float a, float b, float c)
{
float m;
m = a > b ? a : b;
return (m > c ? m : c);
}
void rgb2hsv(uint8_t r, uint8_t g, uint8_t b, float *h, float *s, float *v)
{
float red, green ,blue;
float cmax, cmin, delta;
red = (float)r / 255;
green = (float)g / 255;
blue = (float)b / 255;
cmax = max(red, green, blue);
cmin = min(red, green, blue);
delta = cmax - cmin;
if(delta == 0)
{
*h = 0;
}
else
{
if(cmax == red)
{
if(green >= blue)
{
*h = 60 * ((green - blue) / delta);
}
else
{
*h = 60 * ((green - blue) / delta) + 360;
}
}
else if(cmax == green)
{
*h = 60 * ((blue - red) / delta + 2);
}
else if(cmax == blue)
{
*h = 60 * ((red - green) / delta + 4);
}
}
if(cmax == 0)
{
*s = 0;
}
else
{
*s = delta / cmax;
}
*v = cmax;
}
void hsv2rgb(float h, float s, float v, uint8_t *r, uint8_t *g, uint8_t *b)
{
int hi = ((int)h / 60) % 6;
float f = h * 1.0 / 60 - hi;
float p = v * (1 - s);
float q = v * (1 - f * s);
float t = v * (1- (1 - f) * s);
switch (hi){
case 0:
*r = 255 * v;
*g = 255 * t;
*b = 255 * p;
break;
case 1:
*r = 255 * q;
*g = 255 * v;
*b = 255 * p;
break;
case 2:
*r = 255 * p;
*g = 255 * v;
*b = 255 * t;
break;
case 3:
*r = 255 * p;
*g = 255 * q;
*b = 255 * v;
break;
case 4:
*r = 255 * t;
*g = 255 * p;
*b = 255 * v;
break;
case 5:
*r = 255 * v;
*g = 255 * p;
*b = 255 * q;
break;
}
}
const uint16_t index_wave[300] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12,
13, 13, 14, 14, 15, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, 24, 25, 25, 26, 27, 28, 30, 31, 32, 33,
34, 36, 37, 38, 40, 41, 43, 45, 46, 48, 50, 52, 54, 56, 58, 60, 62, 65, 67, 70, 72, 75, 78, 81, 84, 87, 90,
94, 97, 101, 105, 109, 113, 117, 122, 126, 131, 136, 141, 146, 152, 158, 164, 170, 176, 183, 190, 197, 205,
213, 221, 229, 238, 247, 256, 256, 247, 238, 229, 221, 213, 205, 197, 190, 183, 176, 170, 164, 158, 152, 146,
141, 136, 131, 126, 122, 117, 113, 109, 105, 101, 97, 94, 90, 87, 84, 81, 78, 75, 72, 70, 67, 65, 62, 60, 58,
56, 54, 52, 50, 48, 46, 45, 43, 41, 40, 38, 37, 36, 34, 33, 32, 31, 30, 28, 27, 26, 25, 25, 24, 23, 22, 21, 20,
20, 19, 18, 18, 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6,
6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1};
__align(4) uint8_t rgb[4]={0x0,0x08F,0x0,0x0};
uint32_t * rgbptr;
uint32_t data_index=0;
uint8_t color_r,color_g,color_b;
uint8_t initial_color_r,initial_color_g,initial_color_b;
float color_h,color_s,color_v,temp_v;
uint8_t pangpang=0;
int main(void)
{
SystemInit ();
PB15_INIT(PB15_BTIM0_MAT0);
rgbptr=(uint32_t*)rgb;
BTIM_Init(BTIM0, 16000000);
BTIM_ConfigPWMDataSift(BTIM0, 20, 5, 20, 15);
NVIC_EnableIRQ(BTIM0_IRQn);
BTIM_Init(BTIM1, 10000);
BTIM_ConfigMatch1(BTIM1, 100, TIM_MATCH_RESET_COUNTER|TIM_MATCH_TRIGGER_INT);
NVIC_EnableIRQ(BTIM1_IRQn);
rgb2hsv(rgb[0],rgb[1], rgb[2], &color_h,&color_s,&color_v);
temp_v=(color_v*(float)index_wave[data_index])/256;
hsv2rgb(color_h, color_s, temp_v, &rgb[0],&rgb[1], &rgb[2]);
BTIM0->PPBUF0 =SYS_EndianExChange(*rgbptr);
TIM_START(BTIM0);
TIM_START(BTIM1);
while(1)
{
SYS_Delay(0x4FFFF);
}
}
void BTIM0_IRQHandler(void)
{
if ((BTIM0->IR_b.BUF0BUSY ==0)&&(BTIM0->IR_b .BUF1BUSY ==0))
{
TIM_STOP(BTIM0);
}
BTIM_ClearIntFlag(BTIM0);
}
void BTIM1_IRQHandler(void)
{
data_index++;
if (data_index>299)
{
data_index=0;
PC->NOT_b .NOT4 =1;
}
temp_v=(color_v*(float)index_wave[data_index])/256;
hsv2rgb(color_h, color_s, temp_v, &rgb[0],&rgb[1], &rgb[2]);
pangpang++;
pangpang=pangpang&1;
if (pangpang)
BTIM0->PPBUF1 =SYS_EndianExChange(*rgbptr);
else
BTIM0->PPBUF0 =SYS_EndianExChange(*rgbptr);
TIM_START(BTIM0);
BTIM_ClearIntFlag(BTIM1);
}
效果
3 ADC 读取
ME32G070 提供有 12 位的 ADC 转换器,
主要功能如下:
• 最高2M Hz转换率,12位的A/D转换器
• 支持16个外部AD通道和3个内部信号采样转换
• 支持4路差分输入
• 模块支持低功耗掉电
• ADC测量范围0 ~ VDDA或内部参考电压(缺省值~2V,缺省设置实际值看芯片器件信息(DIA)的VREF)
• 支持突发模式ADC转换
• 可配置ADC转换触发源-输入管脚电平转换或定时器匹配信号
• 8个转换结果存储寄存器,减少中断负担
每个 AD 通道有固定的数据寄存器用于存储转换结果。用户简单使能通道位就可以配置需要的通道进行转换,并把 12 位的转换结果分别保存在对应个寄存器中。一次触发,A/D 转换通道扫描按照从 DR0 到 DR7 次序。
芯片外部输入 ADC_IN0~ ADC_IN6 直接对应 AD0~AD6 通道。
芯片外部输入 ADC_IN7~ ADC_IN18 要通过模拟开关连接到 AD7。
ADC 触发转换模式
若 ADC 的触发转换被触发,所有选中的 A/D 通道都会被转换一次。在完成对所有通道转换之前,不能被中断。若此时发出软件命令或有事件触发 ADC 转换,那么该次 ADC 转换请求会被忽略掉。
ADC转换触发事件可以是软件请求,定时器捕捉 (CAP) 或匹配(MAT)信号,还可以是PWM事件。在定时器捕捉 (CAP) 或匹配(MAT)作触发信号时,信号边沿控制由 CR 寄存器的 EDGE 位决定。
原理
** ADC 在模拟信号转化为数字信号的过程中需要经过****采样**、保持、量化和编码。采样和保持在采样保持电路中完成,而量化和编码步骤则在 ADC 中完成。
- 采样是指 ADC 在一定时间间隔内对连续变化的模拟信号进行取样,实现在有限采样率条件下,无失真还原信号波形信息。取样的频率决定了每秒采集的样本量,通常单位为 Hz。
- ADC 采样保持过程是将已经采集的模拟信号保持恒定时间不变,以便后续模拟信号向数字信号转变,这个过程所使用的电路是采集保持器(SHA)。理想 SHA 由简单开关 SW、保持电容 C 以及驱动电容和后级电路的高输入阻抗缓冲器组成。
- 在A/D转换过程中,采样保持阶段所得到的信号是离散的模拟信号,为了将模拟信号转化为数字信号,需要将采样-保持电路的输出电压按某种方式进行划分到相应的离散电平上,将这一转化过程称为数值量化,简称量化。
- 编码过程就是将量化后的数值按照一定规则用对应代码表示模拟信号波形。
代码
#include "me32g070.h"
#include "me32g070_ioconfig.h"
#include "me32g070_gpio.h"
#include "me32g070_adc.h"
#include "me32g070_uart.h"
#include "me32g070_sys.h"
int main(void)
{
SystemInit ();
PB11_INIT (PB11_ADC_IN15);
ADC_Init(1000000,INTERNALVDDGND);
ADC_SetTrigger(ADC_START_BY_SOFTWAER,0);
ADC_SetupChannels(ADx,TRIGGERMODE);
ADC_SetupAD7Channel(ADC_IN15);
PA2_INIT(PA2_UART1_TX);
PA3_INIT(PA3_UART1_RX);
UART_Open(UART1,115200,UART_NO_PARITY,UART_TRIGGER_LEVEL_1_BYTE);
while(1)
{
ADC_IssueSoftTrigger;
while(ADC->DR7_b .DONE ==0);
UART_PutString (UART1, " ADC15=0x");
UART_PutHex (UART1, ADC->DR7_b.RESULT);
UART_PutString (UART1, "\n");
}
}
效果
ADC 测电压
ADC读取引脚电平并串口发送
#include "me32g070.h"
#include "me32g070_ioconfig.h"
#include "me32g070_gpio.h"
#include "me32g070_adc.h"
#include "me32g070_uart.h"
#include "me32g070_sys.h"
int main(void)
{
uint32_t vdda_vol;
SystemInit ();
PA0_INIT(PA0_ADC_IN0);
ADC_Init(1000000,INTERNALVDDGND);
ADC_SetTrigger(ADC_START_BY_SOFTWAER,0);
ADC_SetupChannels(AD0|ADx,TRIGGERMODE);
ADC_PowerOnVref();
ADC_SetupAD7Channel(ADC_IN_VREF);
PA2_INIT(PA2_UART1_TX);
PA3_INIT(PA3_UART1_RX);
UART_Open(UART1,115200,UART_NO_PARITY,UART_TRIGGER_LEVEL_1_BYTE);
while(1)
{
ADC_IssueSoftTrigger;
while(ADC->DR7_b .DONE ==0);
vdda_vol=ADC->DR7_b.RESULT;
vdda_vol=(DIA->VREF<<12)/vdda_vol;
UART_PutString (UART1, "Vdda Voltage=0x");
UART_PutHex (UART1, vdda_vol);
UART_PutString (UART1, "\n");
}
}
效果
多路 ADC
ADC 7输入选择寄存器(ADC7PREMUX) 描述
0x0: ADC 通道7选择外部ADC_IN7管脚输入
0x1: ADC 通道7选择外部ADC_IN8管脚输入
0x2: ADC 通道7选择外部ADC_IN9管脚输入
0x3: ADC 通道7选择外部ADC_IN10管脚输入
0x4: ADC 通道7选择外部ADC_IN11管脚输入
0x5: ADC 通道7选择内部VDDH分压
0x6: ADC 通道7选择ADC Vref电压
0x7: ADC 通道7选择VDD15(缺省值)
0x8: ADC 通道7选择外部ADC_IN15管脚输入
0x9: ADC 通道7选择外部ADC_IN16管脚输入
0xA: ADC 通道7选择外部ADC_IN17管脚输入
0xB: ADC 通道7选择外部ADC_IN18管脚输入
其他,保留
代码
#include "me32g070.h"
#include "me32g070_ioconfig.h"
#include "me32g070_gpio.h"
#include "me32g070_adc.h"
#include "me32g070_uart.h"
#include "me32g070_sys.h"
int main(void)
{
SystemInit ();
PA0_INIT (PA0_ADC_IN0);
PA1_INIT (PA1_ADC_IN1);
PA2_INIT (PA2_ADC_IN2);
PA3_INIT (PA3_ADC_IN3);
PA4_INIT (PA4_ADC_IN4);
PA5_INIT (PA5_ADC_IN5);
PA6_INIT (PA6_ADC_IN6);
PA7_INIT (PA7_ADC_IN7);
ADC_Init(1000000,INTERNALVDDGND);
ADC_SetTrigger(ADC_START_BY_SOFTWAER,0);
ADC_SetupChannels(AD0|AD1|AD2|AD3|AD4|AD5|AD6|ADx,TRIGGERMODE);
PA2_INIT(PA2_UART1_TX);
PA3_INIT(PA3_UART1_RX);
UART_Open(UART1,115200,UART_NO_PARITY,UART_TRIGGER_LEVEL_1_BYTE);
while(1)
{
ADC_IssueSoftTrigger;
while(ADC->DR7_b .DONE ==0);
UART_PutString (UART1, "AD0=0x");
UART_PutHex (UART1, ADC->DR0_b.RESULT);
UART_PutString (UART1, " AD1=0x");
UART_PutHex (UART1, ADC->DR1_b.RESULT);
UART_PutString (UART1, " AD2=0x");
UART_PutHex (UART1, ADC->DR2_b.RESULT);
UART_PutString (UART1, " AD3=0x");
UART_PutHex (UART1, ADC->DR3_b.RESULT);
UART_PutString (UART1, " AD4=0x");
UART_PutHex (UART1, ADC->DR4_b.RESULT);
UART_PutString (UART1, " AD5=0x");
UART_PutHex (UART1, ADC->DR5_b.RESULT);
UART_PutString (UART1, " AD6=0x");
UART_PutHex (UART1, ADC->DR6_b.RESULT);
UART_PutString (UART1, " AD7=0x");
UART_PutHex (UART1, ADC->DR7_b.RESULT);
UART_PutString (UART1, "\n");
}
}
效果
4 总结
本文通过具体工程案例,简要介绍和测评了敏矽微 ME32G070 单片机的串口通信、PWM 和 ADC功能,为后面的开发和 DIY 项目打下基础。