本帖最后由 abner_ma 于 2023-1-13 10:23 编辑

     MM32L0130 为灵动微推出经济比高的微控制器,搭载 Arm® Cortex®-M0+ 内核,最高工作频率可达 48MHz。内置64KB 高速存储器,并集成了丰富的 I/O 端口和外设模块。包含 1 个 12 位的 ADC、1 个比较器、2 个 16 位通用定时器、2 个 16 位基本定时器、1 个低功耗定时器和 1 个 RTC 计数器,还包含标准的通信接口:2 个 UART 接口、1 个低功耗 UART 接口、2 个 SPI 接口、2 个 I2S 接口和 1 个 I2C 接口。此外,本产品还内置了段码式液晶驱动模块(SLCD)和红外信号调制模块(IRM)。宽温度工作电压为 1.8V ∼ 5.5V,工作温度范围(环境温度)为 -40°C ∼ +85°C。内置多种省电工作模式保证低功耗应用的要求。  基于MM32L013X的医疗级温控系统设计控温设计:  低温疗法是一种以物理方法将患者的体温降低到预期水平而达到治疗疾病目的的方法。临床深低温治疗的应用和研究由来已久,低温在心外科和神经外科手术中已得到广泛应用,并取得良好的脑保护作用,但体温低于28℃时,常诱发心律失常、凝血机制障碍等严重并发症。研究发现脑温下降2~3℃(亚低温)对缺血性脑捐伤也有保护作用,目无深低温所致的各种并发症,使低温治疗重新引起人们的兴趣。使用亚低温(30-35℃)治疗脑缺血、脑缺氧和脑出血病人,取得了令人嘱目的研究成果,认为低温(32.5-35℃)可降低颅内压。
  系统采用PID控制进行调温:通过温度传感器反馈的信号,对Pvar、Ivar、Dvar(比例、积分、微分)三个参数进行整定与优化,调整制冷水温,以此来实现对头部或手部的精确控温。

  开发板如下;功能强大,多路串口引出:

5.jpg

  MM32L013X12位ADC,多达10的通道,可以走DMA

0.jpg


2.jpg
1.jpg

3.jpg
  温度传感器多路NTC 10K采样电路设计:水滴头防水,精度高,价格低廉。需要NTC串接一个10K电阻并在电源两端, MCU的ADC采集RT_INT上电压再经过欧姆定律求出NTC的阻值, 通过温度表或公式就可求出温度值, 通过公式计算是比较麻烦的且误差比查表大, 因此查表是一个不错的实现方式, 虽然会占用MCU的flash较多,精度和稳定性好, 主要它每摄氏度之间相差在几百Ω的距离, 因此外延线可较长, 不易受干扰。采集的温度精度还取决于MCU的ADC, 主要包括ADC的分辨率和参考电压,灵动微MM32L0130的ADC, 单位LSB指的一个12位分辨率的ADC共有4096数字位(2的12次幂等于4096)的其中一位。如综合误差在3.4LSB, 在3.3V参考电压下, 该误差电压是3.3V/ 4096 *3.4LSB≈ 3mV, 再通过NTC温度表逆推看是否可忽略该误差。

7.jpg

   液晶屏采用3.2寸,SPI+DMA硬件驱动,可以做绚丽的GUI界面。

4.jpg

关键代码ADC 配置:
  • void app_adc_init(void)
  • {
  •     /* pins and clock are already in the pin_init.c and clock_init.c. */
  •     /* setup the converter. */
  •     ADC_Init_Type adc_init;
  •     adc_init.Resolution = ADC_Resolution_12b;
  •     adc_init.ClockDiv = ADC_ClockDiv_16;
  •     adc_init.ConvMode = ADC_ConvMode_SingleSlot;
  •     adc_init.Align = ADC_Align_Right;
  •     ADC_Init(BOARD_ADC_PORT, &adc_init);
  •     ADC_Enable(BOARD_ADC_PORT, true); /* power on the converter. */
  •     ADC_EnableTempSensor(BOARD_ADC_PORT, true); /* enable the temperature sensor. */
  •     /* setup the sequence, a regular conversion with only one channel. */
  •     ADC_RegSeqConf_Type adc_regseq_conf;
  •     adc_regseq_conf.SeqSlots = 1u << BOARD_ADC_CHN_NUM;
  •     adc_regseq_conf.SeqDirection = ADC_RegSeqDirection_LowFirst;
  •     ADC_EnableRegSeq(BOARD_ADC_PORT, &adc_regseq_conf);
  •     /* set channel sample time. */
  •     ADC_SetChnSampleTime(BOARD_ADC_PORT, BOARD_ADC_CHN_NUM, ADC_SampleTime_Alt7);
  • }
  • /* software tirgger the adc converter and start the sequence conversion. */
  • uint32_t app_adc_run_conv(void)
  • {
  •     uint32_t data;
  •     uint32_t flags;
  •     uint32_t adc_channel; /* keep the actual hardware conversion channel number. */
  •     /* software tirgger the conversion. */
  •     ADC_DoSwTrigger(BOARD_ADC_PORT, true);
  •     /* wait while the conversion is ongoing. */
  •     while( 0u == (ADC_GetStatus(BOARD_ADC_PORT) & ADC_STATUS_CONV_SEQ_DONE) )
  •     {}
  •     ADC_ClearStatus(BOARD_ADC_PORT, ADC_STATUS_CONV_SEQ_DONE);
  •     data = ADC_GetConvResult(BOARD_ADC_PORT, &adc_channel, &flags);
  •     if (0u == (flags & ADC_CONV_RESULT_FLAG_VALID) )
  •     {
  •         data = 0u; /* the data is unavailable when the VALID flag is not on. */
  •     }
  •     return data;
  • }
  • 复制代码
    NTC任敏电阻采集,查表+中值滤波算法

    const u32 g_rvTable[TABLE_SIZE] = {
  •         1034600, 959006, 889452, 825419, 766434,                                                                                         //-55~-51
  •         712066, 661926, 615656, 572934, 533466, 496983, 463240, 432015, 403104, 376320,         //-50~-41
  •         351495, 328472, 307110, 287279, 268859, 251741, 235826, 221021, 207242, 194412,         //-40~-31
  •         182460, 171320, 160932, 151241, 142196, 133750, 125859, 118485, 111589, 105139,         //-30~-21
  •         99102, 93450, 88156, 83195, 78544, 74183, 70091, 66250, 66643, 59255,                                 //-20~-11
  •         56071, 53078, 50263, 47614, 45121, 42774, 40563, 38480, 36517, 34665,                                 //-10~-1
  •         32919,                                                                                                                                                                 //0
  •         31270, 29715, 28246, 26858, 25547, 24307, 23135, 22026, 20977, 19987,                                 //1~10
  •         19044, 18154, 17310, 16510, 15752, 15034, 14352, 13705, 13090, 12507,                                 //11~20
  •         11953, 11427, 10927, 10452, 10000, 9570, 9161, 8771, 8401, 8048,                                         //21~30
  •         7712, 7391, 7086, 6795, 6518, 6254, 6001, 5761, 5531, 5311,                                                 //31~40
  •         5102, 4902, 4710, 4528, 4353, 4186, 4026, 3874, 3728, 3588,                                                 //41~50
  •         3454, 3326, 3203, 3085, 2973, 2865, 2761, 2662, 2567, 2476,                                                 //51~60
  •         2388, 2304, 2223, 2146, 2072, 2000, 1932, 1866, 1803, 1742,                                                 //61~70
  •         1684, 1627, 1573, 1521, 1471, 1423, 1377, 1332, 1289, 1248,                                                 //71~80
  •         1208, 1170, 1133, 1097, 1063, 1030, 998, 968, 938, 909,                                                         //81~90
  •         882, 855, 829, 805, 781, 758, 735, 714, 693, 673,                                                                         //91~100
  •         653, 635, 616, 599, 582, 565, 550, 534, 519, 505,                                                                         //101~110
  •         491, 478, 465, 452, 440, 428, 416, 405, 395, 384,                                                                         //111~120
  •         374, 364, 355, 345, 337                                                                                                                                //121~125
  • };
  • u16 mid_filter(u16 *collection, u16 size)
  • {
  •         u16 e;
  •         u16 i, j;
  •         u8 sorted;
  •         
  •         i = 0;
  •         while(i < size - 1){//冒泡的趟数
  •                 sorted = 1;
  •                 j = 0;
  •                 while(j < size - 1 - i){//一趟冒的次数
  •                         
  •                         if(collection[j] > collection[j + 1]){
  •                                 e = collection[j];
  •                                 collection[j] = collection[j + 1];
  •                                 collection[j + 1] = e;
  •                                 sorted = 0;
  •                         }
  •                         
  •                         j++;
  •                 }
  •                 if(sorted) break;
  •                
  •                 i++;
  •         }
  •         
  •         return collection[size / 2];
  • }
  • s8 ntc_get_temp(void)
  • {
  •         s16 i;
  •         u32 adcValue;
  •         float rValue;
  •         s8 temperature;
  •         u16 adc_buffer[ADC1_CONVDATA_SIZE];
  •         
  •         for(i = 0; i < ADC1_CONVDATA_SIZE; i++)
  •         {
  •                 //adc_buffer[i] = GetAdcAverage(5);
  •                 adc_buffer[i] =app_adc_run_conv() ;
  •                 delay_ms(1);
  •                
  •         }
  •         adcValue = mid_filter(adc_buffer, ADC1_CONVDATA_SIZE);
  •         rValue = 4095000.0;
  •         rValue = rValue / adcValue - 1000;
  •         adcValue = 10000000.0 / rValue;
  •         for(i = 0; i < TABLE_SIZE; i++) {
  •                 if(adcValue > g_rvTable[i]) {
  •                         temperature = i - 55;
  •                         break;
  •                 }
  •         }               
  •         return temperature;
  • }
  • 复制代码
    PID温控算法:

    #define                D_SCALE                10                //结果放大倍数, 放大10倍就是保留一位小数
  • int        get_temperature(uint adc)
  • {
  •         uint        code *p;
  •         uint        i;
  •         uchar        j,k;
  •         uchar        min,max;        //查表序号, 0对应-40度, 160对应120度
  •         
  • //        adc = 4096 - adc;        //Rt接地
  •         p = temp_table;
  •         if(adc < p[0])                return (0xfffe);
  •         if(adc > p[160])        return (0xffff);
  •         
  •         min = 0;                //-40度
  •         max = 160;                //120度
  •         for(j=0; j<5; j++)        //对分查表
  •         {
  •                 k = min / 2 + max / 2;
  •                 if(adc <= p[k])        max = k;
  •                 else                        min = k;
  •         }
  •                  if(adc == p[min])        i = (uint)min * D_SCALE;
  •         else if(adc == p[max])        i = (uint)max * D_SCALE;
  •         else        // min < temp < max
  •         {
  •                 while(min <= max)
  •                 {
  •                         min++;
  •                         if(adc == p[min])        {i = (uint)min * D_SCALE;        break;}
  •                         else if(adc < p[min])        //线性插补
  •                         {
  •                                 min--;
  •                                 i = p[min];        //min
  •                                 j = (adc - i) * D_SCALE / (p[min+1] - i);
  •                                 i = min;
  •                                 i *= D_SCALE;
  •                                 i += j;
  •                                 break;
  •                         }
  •                 }
  •         }
  •         return (i-400);
  • }
  •         
  •         void temp_control()//控制温度上下限函数
  • {
  •         if(limit_choise==0)//选择按键
  •         {
  •                 delay_ms(1);
  •                 if(limit_choise==0)
  •                 {
  •                         while(!limit_choise);
  •                         limit_choise_num++;
  •                         if(limit_choise_num>=2)
  •                         {
  •                                 limit_choise_num=0;
  •                         }
  •                 }
  •         }
  •         if(limit_choise_num==0)//正常显示
  •         {
  •         Display_GB2312_String(0,90,3, "水箱设定温度",WHITE); //32x32汉字
  •         
  •         }
  •                         
  •         if(limit_choise_num==1)//调节上限温度
  •         {
  •         Display_GB2312_String(0,90,3, "水箱温度调节",RED); //32x32汉字
  •                 if(increase_temperature==0)//增加温度
  •                 {
  •                         delay_ms(1);
  •                         if(increase_temperature==0)
  •                         {
  • //                                while(!increase_temperature);
  •                                 up_limit_temp+=0.5;
  •                                 if(up_limit_temp>=100)
  •                                 {
  •                                         up_limit_temp=0;
  •                                 }
  •          sprintf(fs,"%.2f",up_limit_temp);
  •                             Display_Asc_String(200,90,5, fs,WHITE);  //ASC 7X8点阵
  •                         }
  •                 }
  •                 if(reduce_temperature==0)//减少温度
  •                 {
  •                         delay_ms(1);
  •                         if(reduce_temperature==0)
  •                         {
  • //                                while(!reduce_temperature);
  •                                 up_limit_temp-=0.5;
  •                                 if(up_limit_temp<0)
  •                                 {
  •                                         up_limit_temp=99;
  •                                 }
  •        sprintf(fs,"%.2f",up_limit_temp);
  •                         Display_Asc_String(200,90,5, fs,WHITE);  //ASC 7X8点阵
  •                         }
  •                 }
  •         }
  •         
  •         
  • }
  • 复制代码

       系统可以物联网通信:NB IOT BC26 AT 指令通过HTTP连接onenet平台发送温度实现远程控制;

       一路接NB模组,一路接433M 无线通讯。实现断网情况下跟电脑无线通信,不依赖运行商。
    AT+NSOSD=1,307,504F5354202F646576696365732F33393939343933312F64617461706F696E747320485454502F312E310D0A6170692D6B65793A59414A34626B6C64796F6671657558767871473D6C54416661796B3D0D0A486F73743A6170692E6865636C6F7564732E636F6D0D0A436F6E74656E742D4C656E6774683A3137360D0A0D0A7B226461746173747265616D73223A5B7B226964223A2254656D70222C2264617461706F696E7473223A5B7B2276616C7565223A32387D5D7D2C7B226964223A2268756D69222C2264617461706F696E7473223A5B7B2276616C7565223A31317D5D7D2C7B226964223A22475053222C2264617461706F696E7473223A5B7B2276616C7565223A7B226C6174223A32322E3439393933332C226C6F6E223A3130382E3131323434317D7D5D7D5D7D0D0A0D0A0D0A
  • OK
  • +NSONMI:1,203//这是服务器返回来的数据。
  • AT+NSORF=1,203//查询接收内容的指令203是需要查询的长度。可以多,
  • 1,183.230.40.33,80,203,485454502F312E31203230302
  • 04F4B0D0A446174653A205475652C2030342053657020323031382030393A323
  • 53A353120474D540D0A436F6E74656E742D547970653A206170706C696361746
  • 96F6E2F6A736F6E0D0A436F6E74656E7
  • 42D4C656E6774683A2032360D0A436F6E6E656374696F6E3A206B6565702D616
  • C6976650D0A5365727665723A204170616368652D436F796F74652F312E310D0
  • A507261676D613A206E6F2D63616368650D0A0D0A7B226572726E6F223A302C2
  • 26572726F72223A2273756363227D,0
  • OK
  • AT+NSOCL=1 //关闭连接
  • OK
  • 复制代码
    9.jpg


      基于C++的上位机设计:

    B.jpg

    A.jpg