1、MM32L0136介绍
使用高性能的 Arm? Cortex-M0+ 的 32 位微控制器,最高工作频率可达 48MHz,内置高速存储器,丰富的增强型 I/O 端口和多种外设。
- 64KB Flash,8KB SRAM
- 内置段码式液晶驱动 SLCD,可驱动 8x36 或 4x40 个段码,支持 COM 和 SEG 引脚重映射,占空比、偏压、帧率和对比度等灵活可调,内置电荷泵可实现在电压下降时依然保持液晶屏清晰
- 3 个 UART 接口(包含1个LPUART接口)、2 个 SPI 接口、2 个 I2S 接口、1 个 I2C 接口
- 1 个红外信号调制模块 IRM,支持 ASK、PSK 或 FSK 调制
- 5 通道 DMA
- 5 个 16 位定时器
- 硬件日历 RTC
- 1 个 12 位 SAR ADC,配置 15 个外部通道,转换速率可达 1MSPS
- 1 个低功耗比较器
- 工作电压为 1.8V - 5.5V
- 支持的温度范围为 -40℃ - 85 ℃
- 多种省电工作模式支持低功耗应用的需求
- 待机(Standby)模式下功耗可低至 300nA
- 关机(Shutdown)模式下功耗可低至 100nA
- 提供 LQFP64 和 LQFP48 封装
适合于多种应用场合:
- 空调遥控器
- 温控器
- 耳、额温枪
- 便携医疗设备
- 气、水、热表
- 小家电
2、安装环境的搭建
评估板说明&用户指南:https://www.mindmotion.com.cn/support/development_tools/evaluation_boards/evboard/mm32l0136c7p/
库函数与例程文件:SDK 软件请从 https://mindsdk.mindmotion.com.cn 下载
MM32L0136支持MDK、IAR、GCC方式编译,需要从网页选择对应的环境下载例程和编译软件扩展,一路顺利安装没有问题。
本人开发环境搭建如下:
MDK-ARM 5.35.0.2 + ulink2 v2.03
直接下载MM32_KEIL_Pack.zip的扩展包,选择MM32L01136CP的Device
3、红外控制介绍
对于控制方式直接参考和借鉴了qinyunti的【灵动微电子 L0136 温控器/遥控器应用】万能遥控器实现,先表示感谢!
遥控器应用
开发板EVB-L0136的红外收发接到了串口UART1,设计的初衷是使用串口进行红外收发的,对于自定义协议来说是没问题的,通过TX串口发送即可。
TX发送为低时载波被拉低,TX发送为高时载波就输出。接收因为是接收二极管已经从载波中滤出信号,直接串口接收即可。
但是格力空调遥控器的协议这样实际是不行的,使用的是不同占空比的脉宽代表0和1。
所以按qinyunti的方式,选择PA7产生38KHz的PWM载波信号,然后PA7连接到PA9。
这里要说明下,红外发送管使用的是IR26-61C/L510,内部已经带有滤波,直接就是输出解码后的数字信号。
检查到载波信号就输出0,没有信号则输出1。
4、格力协议介绍
对于格力协议的介绍有好多,不过多的进行描述,其结构如下:
需要说明是,协议中的数据都是逆序,低位字节在前,高位在后。
其校验码的计算网上说明的大都不对,其计算如下:
校验码 = (模式) + (温度 - 16) + 定时2 + 10 + 左右扫风 - 开关 * 8
5、程序编码
PWM初始化部分:
void BOARD_InitPWM(void)
{
RCC_EnableAPB2Periphs( RCC_APB2_PERIPH_TIM17, 1 );
RCC_ResetAPB2Periphs( RCC_APB2_PERIPH_TIM17 );
/* Setup the counter counting step. */
TIM_Init_Type tim_init;
tim_init.ClockFreqHz = 48000000;
tim_init.StepFreqHz = 1000000;
tim_init.Period = 26 - 1; /* the counter would return to the base on next step. */
tim_init.EnablePreloadPeriod = false; /* no need preload, load period value immediately. */
tim_init.PeriodMode = TIM_PeriodMode_Continuous;
tim_init.CountMode = TIM_CountMode_Increasing;
TIM_Init( (TIM_Type *)TIM17, &tim_init );
/* Setup the PWM output channel. */
TIM_OutputCompareConf_Type tim_out_conf;
tim_out_conf.ChannelValue = 0u;
tim_out_conf.EnableFastOutput = false;
tim_out_conf.EnablePreLoadChannelValue = false; /* disable preload, load channel value immediately. */
tim_out_conf.RefOutMode = TIM_OutputCompareRefOut_FallingEdgeOnMatch;
tim_out_conf.ClearRefOutOnExtTrigger = false;
tim_out_conf.PinPolarity = TIM_PinPolarity_Rising;
TIM_EnableOutputCompare( (TIM_Type *)TIM17, TIM_CHN_1, &tim_out_conf );
TIM_PutChannelValue( (TIM_Type *)TIM17, TIM_CHN_1, 13 );
/* Start the output compare, only available for the TIM peripheral with this feature. */
TIM_EnableOutputCompareSwitch( (TIM_Type *)TIM17, true );
/* Start the counter. */
//TIM_Start( (TIM_Type *)TIM17 );
}
IO配置
void BOARD_InitPins(void)
{
GPIO_Init_Type gpio_init;
/* PA3 - UART2_RX. */
gpio_init.Pins = GPIO_PIN_3;
gpio_init.PinMode = GPIO_PinMode_In_Floating;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);
/* PA2 - UART2_TX. */
gpio_init.Pins = GPIO_PIN_2;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConf(GPIOA, gpio_init.Pins, GPIO_AF_1);
/* KEY 0 - 3 */
gpio_init.Pins = BOARD_KEY0_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_In_PullDown;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_KEY0_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_KEY0_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_KEY1_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_In_PullUp;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_KEY1_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_KEY1_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_KEY2_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_In_PullUp;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_KEY2_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_KEY2_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_KEY3_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_In_PullUp;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_KEY3_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_KEY3_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
/* LED 0 - 3 */
gpio_init.Pins = BOARD_LED0_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_LED0_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_LED0_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_LED1_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_LED1_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_LED1_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_LED2_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_LED2_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_LED2_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
gpio_init.Pins = BOARD_LED3_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_Out_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_LED3_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_LED3_GPIO_PORT, gpio_init.Pins, GPIO_AF_15);
/* PWM */
gpio_init.Pins = BOARD_PWM_GPIO_PIN;
gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
gpio_init.Speed = GPIO_Speed_50MHz;
GPIO_Init(BOARD_PWM_GPIO_PORT, &gpio_init);
GPIO_PinAFConf(BOARD_PWM_GPIO_PORT, gpio_init.Pins, GPIO_AF_5);
}
其它按gpio例程配置没有修改
主程序:
/*
* Definitions.
*/
/* PWM start/stop */
#define ir_outh() TIM_Stop( (TIM_Type *)TIM17 )
#define ir_outl() TIM_Start( (TIM_Type *)TIM17 )
/*
* Declerations.
*/
/* 定义信号的脉冲宽度 (us) */
#define MAX_IR_INDEX 5
const uint32_t IR_D_HOLDTIME[MAX_IR_INDEX][2] =
{ {600, 600}, {600, 1600}, {9000, 4500}, {600, 20000}, { 600, 40000 } };
#define IR_D0_INDEX 0
#define IR_D1_INDEX 1
#define IR_S_INDEX 2
#define IR_C_INDEX 3
#define IR_R_INDEX 4
/*
* 发送数据数组定义
*/
static uint32_t s_ir_index = 0;
static uint8_t s_ir_order = 0;
static uint32_t s_ir_num = 0;
static uint32_t s_ir_done = 1;
#define MAX_IO_STATE 256
static uint8_t s_iostate[MAX_IO_STATE];
/*
* TIM16定义,计算时间使用 1us
*/
void time_init( uint32_t period )
{
TIM_Init_Type timinit;
TIM_Stop( (TIM_Type *)TIM16 );
RCC_EnableAPB2Periphs( RCC_APB2_PERIPH_TIM16, 1 );
RCC_ResetAPB2Periphs( RCC_APB2_PERIPH_TIM16 );
timinit.ClockFreqHz = 48000000;
timinit.StepFreqHz = 1000000;
timinit.Period = period;
timinit.EnablePreloadPeriod = false;
timinit.PeriodMode = TIM_PeriodMode_Continuous; //TIM_PeriodMode_OneTimeRun;
timinit.CountMode = TIM_CountMode_Increasing;
TIM_Init( (TIM_Type *)TIM16, &timinit );
TIM_DoSwTrigger( (TIM_Type *)TIM16, 1u << 0 );
TIM_ClearInterruptStatus( (TIM_Type *)TIM16, TIM_GetInterruptStatus((TIM_Type *)TIM16) );
TIM_EnableInterrupts( (TIM_Type *)TIM16, TIM_INT_UPDATE_PERIOD, 1 );
NVIC_EnableIRQ( TIM16_IRQn );
TIM_Start( (TIM_Type *)TIM16 );
}
/*
* 载波的发送
* 这里要注意TIM_PutChannelValue( (TIM_Type *)TIM17, TIM_CHN_1, 0 )的配置
* 在PWM停止后保证载波的输出为低电平
*/
void ir_handle( void )
{
GPIO_Init_Type gpio_init;
if( s_ir_index < s_ir_num )
{
if( ( s_ir_order <= 1 ) && ( s_iostate[s_ir_index] < MAX_IR_INDEX ) )
{
if( s_ir_order == 0 )
{
TIM_PutChannelValue( (TIM_Type *)TIM17, TIM_CHN_1, 13 );
ir_outl();
}
else
{
TIM_PutChannelValue( (TIM_Type *)TIM17, TIM_CHN_1, 0 );
ir_outh();
}
time_init( IR_D_HOLDTIME[s_iostate[s_ir_index]][s_ir_order] );
}
else
{
TIM_Stop( (TIM_Type *)TIM16 );
s_ir_done = 1;
s_ir_num = 0;
s_ir_index = 0;
s_ir_order = 0;
}
if( s_ir_order < 1 )
{
s_ir_order++;
}
else
{
s_ir_index++;
s_ir_order = 0;
}
}
else
{
TIM_Stop( (TIM_Type *)TIM16 );
s_ir_done = 1;
s_ir_num = 0;
s_ir_index = 0;
s_ir_order = 0;
}
}
/*
* 生成协议
*/
/*
* key 开关 0 ~ 1
* mode 模式 0 ~ 4
* fanspeed 风速 0 ~ 3
* temperature 温度 16 ~ 30
*/
uint8_t build_gree_checksum( int mode, int key, int fanspeed, int temperture )
{
uint32_t i;
i = mode + ( temperture - 16) + 10 - key * 8;
return (uint8_t)( i & 0xF );
}
uint32_t build_gree_data( int mode, int key, int fanspeed, int temperture )
{
uint8_t chk;
int i;
i = 0;
// start
s_iostate[i++] = IR_S_INDEX;
// 35bits
// mode
s_iostate[i++] = (mode >> 0) & 0x1;
s_iostate[i++] = (mode >> 1) & 0x1;
s_iostate[i++] = (mode >> 2) & 0x1;
// key
s_iostate[i++] = (key >> 0) & 0x1;
// fanspeed
s_iostate[i++] = (fanspeed >> 0) & 0x1;
s_iostate[i++] = (fanspeed >> 1) & 0x1;
// scan / sleep
s_iostate[i++] = 0;
s_iostate[i++] = 0;
// temperture
s_iostate[i++] = ((temperture - 16) >> 0) & 0x1;
s_iostate[i++] = ((temperture - 16) >> 1) & 0x1;
s_iostate[i++] = ((temperture - 16) >> 2) & 0x1;
s_iostate[i++] = ((temperture - 16) >> 3) & 0x1;
// time
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
// super / lamp / health / dry
s_iostate[i++] = 0;
s_iostate[i++] = 1;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
// reserve
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
// reserve
s_iostate[i++] = 1;
s_iostate[i++] = 0;
s_iostate[i++] = 1;
s_iostate[i++] = 0;
// reserve
s_iostate[i++] = 0;
s_iostate[i++] = 1;
s_iostate[i++] = 0;
// contiune
s_iostate[i++] = IR_C_INDEX;
.....
// reserve
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
s_iostate[i++] = 0;
// check
chk = build_gree_checksum( mode, key, fanspeed, temperture );
s_iostate[i++] = ( chk >> 0 ) & 0x1;
s_iostate[i++] = ( chk >> 1 ) & 0x1;
s_iostate[i++] = ( chk >> 2 ) & 0x1;
s_iostate[i++] = ( chk >> 3 ) & 0x1;
// contiune
s_iostate[i++] = IR_C_INDEX;
return i;
}
/*
* 按键发送控制
*/
int main(void)
{
int key0_sta, key1_sta, key2_sta, key3_sta;
int num;
BOARD_Init();
printf("\r\ngpio_basic example.\r\n");
GPIO_WriteBit( BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 1u );
GPIO_WriteBit( BOARD_LED1_GPIO_PORT, BOARD_LED1_GPIO_PIN, 1u );
GPIO_WriteBit( BOARD_LED2_GPIO_PORT, BOARD_LED2_GPIO_PIN, 1u );
GPIO_WriteBit( BOARD_LED3_GPIO_PORT, BOARD_LED3_GPIO_PIN, 1u );
key0_sta = key1_sta = key2_sta = key3_sta = 0;
while(1)
{
if( GPIO_ReadInDataBit( BOARD_KEY0_GPIO_PORT, BOARD_KEY0_GPIO_PIN ) == 1 )
{
/* key is pressed. */
GPIO_WriteBit( BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 0u );
key0_sta = 1;
}
else if( key0_sta == 1 )
{
/* send ir, mode auto */
num = build_gree_data( 0, 1, 0, 25 );
ir_start( num );
while( ir_isdone() == 0 ) mdelay( 200 );
GPIO_WriteBit( BOARD_LED0_GPIO_PORT, BOARD_LED0_GPIO_PIN, 1u );
key0_sta = 0;
}
if( GPIO_ReadInDataBit( BOARD_KEY1_GPIO_PORT, BOARD_KEY1_GPIO_PIN ) == 0 )
{
/* key is pressed. */
GPIO_WriteBit( BOARD_LED1_GPIO_PORT, BOARD_LED1_GPIO_PIN, 0u );
key1_sta = 1;
}
else if( key1_sta == 1 )
{
/* send ir, mode cool */
num = build_gree_data( 1, 1, 0, 20 );
ir_start( num );
while( ir_isdone() == 0 ) mdelay( 200 );
GPIO_WriteBit( BOARD_LED1_GPIO_PORT, BOARD_LED1_GPIO_PIN, 1u );
key1_sta = 0;
}
if( GPIO_ReadInDataBit( BOARD_KEY2_GPIO_PORT, BOARD_KEY2_GPIO_PIN ) == 0 )
{
/* key is pressed. */
GPIO_WriteBit( BOARD_LED2_GPIO_PORT, BOARD_LED2_GPIO_PIN, 0u );
key2_sta = 1;
}
else if( key2_sta == 1 )
{
/* send ir, mode heater */
num = build_gree_data( 4, 1, 0, 24 );
ir_start( num );
while( ir_isdone() == 0 ) mdelay( 200 );
GPIO_WriteBit( BOARD_LED2_GPIO_PORT, BOARD_LED2_GPIO_PIN, 1u );
key2_sta = 0;
}
if( GPIO_ReadInDataBit( BOARD_KEY3_GPIO_PORT, BOARD_KEY3_GPIO_PIN ) == 0 )
{
/* key is pressed. */
//GPIO_WriteBit( BOARD_LED3_GPIO_PORT, BOARD_LED3_GPIO_PIN, 0u );
key3_sta = 1;
}
else if( key3_sta == 1 )
{
/* send ir */
num = build_gree_data( 0, 0, 0, 25 );
ir_start( num );
while( ir_isdone() == 0 ) mdelay( 200 );
//GPIO_WriteBit( BOARD_LED3_GPIO_PORT, BOARD_LED3_GPIO_PIN, 1u );
key3_sta = 0;
}
}
}
发送波形:
接收波形:
6、总结
从目前开发环境的使用看,MM32L0136芯片的各类资源及SDK已经比较完善和成熟,库文件结构也比较清晰,常用寄存器和参数的调用都进行了封装。