本文最后修改时间:2023年06月07日
一、本节简介
本文介绍如何使用MT32F006连接CS1237芯片采集秤传感器。
二、实验平台
库版本:V1.0.0
编译软件:MDK5.37
硬件平台:MT32F006开发板(主芯片MT32F006)
仿真器:JLINK
传感器:CS1237
三、版权声明
1)作者:甜甜的大香瓜
2)声明:喝水不忘挖井人,转载请注明出处。
3)纠错/业务合作:897503845@qq.com
4)香瓜嵌入式之STM8/STM32群:164311667
5)本文出处:原创连载资料《简单粗暴学MT32F006》
6)完整开源资料下载地址(电脑端打开):opengua.taobao.com
四、实验前提
1、在进行本文步骤前,请先阅读以下章节:
1)《简单粗暴学MT32F006》的“第一章和第二章”章节。
2、在进行本文步骤前,请先实现以下章节:
1)《简单粗暴学MT32F006》的《MT32F006之定时器延时》。
2)《简单粗暴学MT32F006》的《MT32F006之串口》。
五、基础知识
1、什么是CS1237?
答:CS1237的完整型号CS1237-SO(封装SOP-8-150mil),品牌是CHIPSEA(芯海科技)。
10hz(1秒100个数据)最慢,但稳定性最好。本文使用40hz。
上图所述,虽然CS1237是24位的ADC芯片,但红框的意思是稳压为5V时有效位是20位,会比稳压为3.3V时有效位19.5位更高。
2、秤传感器是什么?
答:
如上图,它会被用于电子秤内部,锁紧一端,重物压在秤盘上并给力到另一端。
其中:
1、左上角的10,表示这是10kg的传感器。如果给了50kg的重物放上去,就会损坏。
2、中间有两个圈,这是厂家用来调整四个角的均衡度的。会人工或者机器,用一根很粗的金属棒去磨蹭,削掉多余的部分,以此达到四角在一个可接受范围内。
3、白色胶内部有电路板,它会将压力通过四根线转换出电压信号。
本文使用的秤传感器规格书如下:
上述部分数据的解释:
1)非线性误差:10kg*0.05%=5g,也就是这个传感器是5g精度的。但我们还能通过软件算法(求平均值之类的)进一步提高精度。以做到精度超过5g的电子秤。
2)蠕变:塑料被重物压会变形,而固体材料在不是重物的压力下,也会因为时间压得久而变形,这就是蠕变。规格书内写的是每5分钟会有±0.05%的蠕变。
3)安全超载:虽然量程是10kg,但是最多只能超载150%,也就是最多到15kg。也就是厂家多做了一些容错量程。
4)四条线定义:红和黑是一组电源线、绿和白是一组信号线。注意,有的传感器有多一条屏蔽线,这条屏蔽线一端要接GND,另一端要包起来。
3、厂家如果说这个10kg的传感器精度是10万,应该怎么理解这个数据?
答: 意味着10kg的数值,会被分成10万份,每1份=10kg/100000=0.1g。所以也就是我们通俗习惯的精度看法0.1g。
4、厂家提供的样品能达到我们要求,那量产可能会有哪些问题?
答: 厂家的传感器是有精度标准的,例如本文中用的传感器的标准是S(一般精度)、U(好一点精度)、V(最好精度)。厂家提供样品时会从S标准的批次给我们几个样品,但实际测试发现它的标准是V。这会让我们导致误判量产时都是这个标准。
但厂家也不是故意给我们V,只是那个批次以S标准进行生产,没有进一步区分S和V。他们能保证的,是至少给我们S以上的货。
所以,我们要做的:跟厂家确定我们样品的实际标准。
5、如何提高整个采集数据过程的精度?
答:
1)秤传感器的4条线,扭一起缠绕着,会屏蔽一部分外部信号。
2)秤传感器的4条线,外面有屏蔽包裹着,信号会更好。
3)秤传感器的4条线,上面套一个磁环。
4)用5v的稳压芯片给cs1237供电。
5)秤传感器的4条线,直接焊接在pcb上,不要用接插的方式。
六、硬件原理
1、MCU与CS1237连接
由上图可知:
CS1237引脚 | MT32F006引脚 |
DRDY/DOUT | PIO0_5 |
SCLK | PIO0_4 |
注意,DRDY/DOUT是一个IO做输入、输出的切换,来进行数据传输的。
七、实验步骤
1、编写并添加驱动
1)编写驱动GUA_CS1237.c(存放在“……\Projects\MT006_StdPeriph_Templates\GUA”)
//********************************************************************** //name: GUA_CS1237.c //introduce: CS1237驱动 //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2023.05.31 //********************************************************************** #include "mt006.h" #include "GUA_CS1237.h" #include "GUA_Delay.h" #include "GUA_UART.h" /*********************宏定义************************/ #define GUA_OUT_H GPIO_WriteBit(GPIO0, GPIO_Pin_5, Bit_SET) #define GUA_OUT_L GPIO_WriteBit(GPIO0, GPIO_Pin_5, Bit_RESET) #define GUA_CLK_H GPIO_WriteBit(GPIO0, GPIO_Pin_4, Bit_SET) #define GUA_CLK_L GPIO_WriteBit(GPIO0, GPIO_Pin_4, Bit_RESET) #define GUA_OUT_OUT 0 //输出的宏 #define GUA_OUT_IN 1 //输入的宏 /*********************内部函数************************/ static void GUA_CS1237_GPIO_Out(GUA_U8 nGUA_Out_Status); static void GUA_CS1237_GPIO_Init(void); static void GUA_CS1237_WriteConfig(void); //********************************************************************** //name: GUA_CS1237_ShutDownControl //introduce: CS1237 低功耗控制 //parameter: nGUA_ShutDownControl_Status:GUA_SHUTDOWN_ON or GUA_SHUTDOWN_OFF //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2023.05.31 //********************************************************************** void GUA_CS1237_ShutDownControl(GUA_U8 nGUA_ShutDownControl_Status) { //PIO2_3 LDO_EN低电平时,开启低功耗 if(nGUA_ShutDownControl_Status == GUA_SHUTDOWN_ON) { GPIO_WriteBit(GPIO2, GPIO_Pin_3, Bit_RESET); } //PIO2_3 LDO_EN高电平时,关闭低功耗 else { GPIO_WriteBit(GPIO2, GPIO_Pin_3, Bit_SET); } } //********************************************************************** //name: GUA_CS1237_GPIO_Out //introduce: CS1237 OUT引脚的输入和输出切换 //parameter: none //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2022.12.29 //********************************************************************** static void GUA_CS1237_GPIO_Out(GUA_U8 nGUA_Out_Status) { GPIO_InitTypeDef GPIO_InitStructure; //PIO0_5 DOUT设置为输出 if(nGUA_Out_Status == GUA_OUT_OUT) { GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIO0, &GPIO_InitStructure); } //PIO0_5 DOUT设置为输入 else { GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIO0, &GPIO_InitStructure); } } //********************************************************************** //name: GUA_CS1237_GPIO_Init //introduce: CS1237的GPIO初始化 //parameter: none //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2022.12.29 //********************************************************************** static void GUA_CS1237_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; //PIO0_4 SCK,注意不能同时初始化多个IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIO0, &GPIO_InitStructure);
//PIO0_5 DOUT,注意不能同时初始化多个IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIO0, &GPIO_InitStructure);
//PIO2_3 LDO_EN,注意不能同时初始化多个IO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIO2, &GPIO_InitStructure); } //********************************************************************** //name: GUA_CS1237_WriteConfig //introduce: 写cs1237配置 //parameter: none //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2022.12.29 //********************************************************************** static void GUA_CS1237_WriteConfig(void) { GUA_U8 nGUA_I; GUA_U8 nGUA_Data = 0x5C; //REF输出关闭 输出40HZ PGA=128(有效分辨率能达20位) 通道A //GUA_U8 nGUA_Data = 0x4C; //REF输出关闭 输出10HZ PGA=128(有效分辨率能达20位) 通道A GUA_U16 nGUA_Timemout = 0;
//数据建立 GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H; //OUT 1 GUA_CS1237_GPIO_Out(GUA_OUT_IN); //切换OUT引脚为输入 GUA_CLK_L; //CLK 0
//查询OUT脚,等待CS1237准备好,如果超时则退出 while(GPIO_ReadDataBit(GPIO0, GPIO_Pin_5) == SET) { GUA_Delay_Ms(1); if(++nGUA_Timemout > 300) { GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H; GUA_CLK_H; return; } }
//29个clk脉冲 for(nGUA_I=0; nGUA_I<29; nGUA_I++) { GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1); }
//第30~36个脉冲,写配置寄存器是0x65、读配置寄存器是0x56,这里是1100101(7位、0x65) GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//30 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//31 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//32 0 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//33 0 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//34 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//35 0 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//36 1
//第37个脉冲,写入了0x65 GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1);
//第38~45个脉冲,写8位数据 for(nGUA_I=0; nGUA_I<8; nGUA_I++) { GUA_CLK_H; GUA_Delay_Us(1);
if(nGUA_Data&0x80) GUA_OUT_H; else GUA_OUT_L;
nGUA_Data <<= 1;
GUA_CLK_L; GUA_Delay_Us(1); }
//第46个脉冲结束,并释放引脚 GUA_OUT_H; GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1); } //********************************************************************** //name: GUA_CS1237_ReadConfig //introduce: 读cs1237配置 //parameter: none //return: 芯片的配置数据 //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2022.12.29 //********************************************************************** GUA_U8 GUA_CS1237_ReadConfig(void) { GUA_U8 nGUA_I; GUA_U8 nGUA_Data = 0; //读取到的数据 GUA_U16 nGUA_Timemout = 0; //超时数据
//数据建立 GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H; //OUT 1 GUA_CS1237_GPIO_Out(GUA_OUT_IN); //切换OUT引脚为输入 GUA_CLK_L; //CLK 0
//查询OUT脚,等待CS1237准备好,如果超时则退出 while(GPIO_ReadDataBit(GPIO0, GPIO_Pin_5) == SET) { GUA_Delay_Ms(1); if(++nGUA_Timemout > 300) { GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_CLK_H; GUA_OUT_H; return 1; } } //29个clk脉冲 for(nGUA_I=0; nGUA_I<29; nGUA_I++)// 产生第1到29个时钟 { GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1); }
//第30~36个脉冲,写配置寄存器是0x65、读配置寄存器是0x56,这里是1010110(7位、0x56) GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//30 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//31 0 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//32 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//33 0 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//34 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_H;GUA_CLK_L;GUA_Delay_Us(1);//35 1 GUA_CLK_H;GUA_Delay_Us(1);GUA_OUT_L;GUA_CLK_L;GUA_Delay_Us(1);//36 0 //第37个脉冲,写入了0x56 GUA_OUT_H; GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1);
//第38 - 45个脉冲了,读取数据 GUA_CS1237_GPIO_Out(GUA_OUT_IN); //切换OUT引脚为输入 for(nGUA_I=0; nGUA_I<8; nGUA_I++) { //脉冲 GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1);
//读出当前脉冲的高低电平 nGUA_Data <<= 1; if(GPIO_ReadDataBit(GPIO0, GPIO_Pin_5) == SET) nGUA_Data++; }
//第46个脉冲结束,并释放引脚 GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1);
GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H;
return nGUA_Data; } //********************************************************************** //name: GUA_CS1237_ReadADC //introduce: 读取ADC数据 //parameter: none //return: 一个有符号的ADC数据 //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2023.05.10 //********************************************************************** GUA_32 GUA_CS1237_ReadADC(void) { GUA_U8 nGUA_I; GUA_U32 nGUA_Data = 0; //读取到的数据 GUA_U16 nGUA_Timemout = 0; //溢出计时器 GUA_32 nGUA_Temp;
//数据建立 GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H; //OUT 1 GUA_CS1237_GPIO_Out(GUA_OUT_IN); //切换OUT引脚为输入 GUA_CLK_L; //CLK 0
//查询OUT脚,等待CS1237准备好,如果超时则退出 while(GPIO_ReadDataBit(GPIO0, GPIO_Pin_5) == SET) { //延时1ms GUA_Delay_Ms(1);
//超时,则直接退出程序 if(++nGUA_Timemout > 300) { GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_CLK_H; GUA_OUT_H; return 1; } }
//获取24位有效转换 for(nGUA_I=0; nGUA_I<24; nGUA_I++) { GUA_CLK_H; GUA_Delay_Us(1);
nGUA_Data <<= 1; if(GPIO_ReadDataBit(GPIO0, GPIO_Pin_5) == SET) nGUA_Data++;
GUA_CLK_L; GUA_Delay_Us(1); }
//第25~27个脉冲 for(nGUA_I=0; nGUA_I<3; nGUA_I++) { GUA_CLK_H; GUA_Delay_Us(1); GUA_CLK_L; GUA_Delay_Us(1); }
//释放DOUT脚,当下次再被拉低,表明新的数据已经准备好 GUA_CS1237_GPIO_Out(GUA_OUT_OUT); //切换OUT引脚为输出 GUA_OUT_H;
//判断是负数,最高位24位是符号位 if(nGUA_Data&0x00800000) { nGUA_Temp = -(((~nGUA_Data)&0x007FFFFF) + 1);//补码变原码 } //正数的补码就是源码 else { nGUA_Temp = nGUA_Data&0x00ffffff; //去除高8位 } return nGUA_Temp; } //********************************************************************** //name: GUA_CS1237_Init //introduce: CS1237初始化 //parameter: none //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2022.12.29 //********************************************************************** void GUA_CS1237_Init(void) { GUA_U8 nGUA_Config = 0; //读取到的配置数据 GUA_U8 nGUA_ADC_Value = 0; //前期不稳定的adc数据
//gpio初始化 GUA_CS1237_GPIO_Init();
//退出低功耗模式 GUA_CS1237_ShutDownControl(GUA_SHUTDOWN_OFF);
//延时,如果不加延时,全速时会写入不正常 GUA_Delay_Ms(100);
//读cs1237配置,默认0x0C,如果写过的就会是0x5C //nGUA_Config = GUA_CS1237_ReadConfig();
//发送配置 //GUA_UART_Send(nGUA_Config);
//写cs1237配置,写入0x5C GUA_CS1237_WriteConfig(); //读cs1237配置,判断是否0x5C写成功,并发给串口 nGUA_Config = GUA_CS1237_ReadConfig(); GUA_UART_Send(nGUA_Config);
//过滤前几次读出的adc,并发给串口 for(GUA_U8 i=0; i<20; i++) { nGUA_ADC_Value = GUA_CS1237_ReadADC()>>2; printf("%d \n", nGUA_ADC_Value); } } |
2)编写驱动头文件GUA_ CS1237.h
(存放在“……\Projects\MT006_StdPeriph_Templates\GUA”)
//********************************************************************** //name: GUA_CS1237.h //introduce: CS1237驱动的头文件 //author: 甜甜的大香瓜 //email: 897503845@qq.com //shop: opengua.taobao.com //changetime: 2023.05.31 //********************************************************************** #ifndef _GUA_CS1237_H_ #define _GUA_CS1237_H_ /*********************宏定义************************/ //类型宏 #ifndef GUA_C typedef char GUA_C; #endif #ifndef GUA_U8 typedef unsigned char GUA_U8; #endif #ifndef GUA_8 typedef signed char GUA_8; #endif #ifndef GUA_U16 typedef unsigned short GUA_U16; #endif #ifndef GUA_16 typedef signed short GUA_16; #endif #ifndef GUA_U32 typedef unsigned long GUA_U32; #endif #ifndef GUA_32 typedef signed long GUA_32; #endif //低功耗控制宏 #define GUA_SHUTDOWN_ON 0 //开低功耗 #define GUA_SHUTDOWN_OFF 1 //关闭低功耗 /*********************外部函数声明************************/ extern void GUA_CS1237_ShutDownControl(GUA_U8 nGUA_ShutDownControl_Status); extern GUA_U8 GUA_CS1237_ReadConfig(void); extern GUA_32 GUA_CS1237_ReadADC(void); extern void GUA_CS1237_Init(void); #endif |
3)工程中添加驱动文件
4)在MDK设置中添加驱动源文件路径
2、添加库的驱动
1)添加库的驱动文件(为了方便一次性都添加进来,已有则不用添加)
2)添加库的驱动头文件(mt006_conf.h 中)(为了方便一次性都添加进来,已有则不用添加)
3、在应用层中调用
1)添加驱动头文件(main.c中)
#include "GUA_CS1237.h" |
2)添加驱动初始化代码(main.c的main函数中)
//ADC芯片初始化 GUA_CS1237_Init(); |
3)添加测试代码(main.c的main函数中)
u32 gGUA_ADC_Value = 0; gGUA_ADC_Value = GUA_CS1237_ReadADC()>>2; //去掉末尾2位 |
这里只采集原始数据(去掉末尾2位不稳定的),后续可自行使用剩余22位做电子秤相关算法(如校准、求平均值、追零等)。
八、注意事项
暂无
九、实验结果
暂无