本帖最后由 北方西门吹雪 于 2020-7-6 13:38 编辑
【RT-Thread作品秀】智能冰箱作者:北方西门吹雪
概述 智能冰箱是通过采集冰箱内的温度变化,自动调节启动停止方式,实现节能的效果。 使用的硬件是龙芯教育板。基于MIPS指令集的一款完全国产化的SoC集成芯片。搭载 2K1000 处理器(主频 1GHz),板载 DDR3 颗粒,实现 DDR3 的运行存储功能。实现了 GPIO 的输入输出,中断功能。板上集成 1 个网 口,集成 3 个 USB 接口,HDMI 接口,LCD 接口,音频输入/输出,集成 SD 卡接口,集成 2 个 CAN 接口,集成 RTC 计时功能。可以外扩 WIFI 模块。2K 龙芯派可以广泛应用于信息安 全、电力、轨道交通、工业控制、信号处理、数据通信、信息教育等领域。 搭载 2K1000 处理器(主频 1GHz),板载 DDR3 颗粒,实现 DDR3 的运行存储功能。实现了 GPIO 的输入输出,中断功能。板上集成 1 个网 口,集成 3 个 USB 接口,HDMI 接口,LCD 接口,音频输入/输出,集成 SD 卡接口,集成 2 个 CAN 接口,集成 RTC 计时功能。可以外扩 WIFI 模块。2K 龙芯派可以广泛应用于信息安 全、电力、轨道交通、工业控制、信号处理、数据通信、信息教育等领域,选择嵌入式应用是龙芯工业版的领域,这次使用的是龙芯教育版,更多是适合桌面系统。
本项目实现的功能是实时采集环境温度,并根据温度变化及时调整智能冰箱的通断。 补充说明: 最初提出的设计方案,主要包括连接摄像头驱动,导入深度学习引擎tensorflowlite实现图像分割和图像分类的方式,自动记录冰箱内的物品。因为开发的时间短,提供的RTT硬件驱动只有GPIO和UART,因此主要的时间用在RTT的BSP部分开发,主要时间用于完成了I2C的驱动开发,智能完成智能冰箱温控的基本功能。 RTT调试口使用RS232-UART0。同时编译调试需要进行如下修改, rtconfig.py,使用自己安装后的路径。 # EXEC_PATH = r'D:\Development\DesignParks\codebench\bin' 修改SConstruct这个文件: rtconfig.AFLAGS += ' -I' + RTT_ROOT + '/libcpu/mips/common' rtconfig.AFLAGS += ' -I' + str(Dir('#')) 可以写入U盘,然后进入pmon加载 1. load /dev/fs/fat@usb0/rtthread.elf 2. 或者load (usb0,0)/rtthread.elf 3. g
RT-Thread使用情况概述 RTT内核,使用了I2C,GPIO的硬件驱动,同时使用了thread线程管理,实现RTOS的基本功能。其他组件,软件包,均没有采用。主要是因为适应的驱动生态不够完善,也没有测试,短时还不具备使用条件。 主要的工作在驱动编写和测试,因为驱动编写的过程比较长,所有按照反方向介绍,更容易理解。所以总结一下RTT的i2c驱动编写过程。 1. RTT的驱动和开发板的驱动之间是需要一个函数连接,这样使用标准RTT的语句,就重定向到了开发板的专用驱动函数中。不同的硬件,需要不同的语句,对于I2C,需要执行的是总线注册, rt_i2c_bus_device_register(&l2k_i2c->parent, "i2c0"); 这个语句把如下结构体关联到了标准RTT的i2c语句中, static const struct rt_i2c_bus_device_ops l2k_i2c_ops =
{
rt_i2c_master_xfer,
RT_NULL,
rt_i2c_bus_control
};
在标准的RTT驱动中,i2c就是通过这样的注册过程,重定向到了在BSP目录下的对应driver。 2. 然后就是上述对应函数的编写, rt_size_t rt_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg *msgs, rt_uint32_t num)
{
struct l2k_i2c_bus * i2c_bus = (struct l2k_i2c_bus *)bus;
l2k_i2c_info_t i2c_info;
struct rt_i2c_msg *msg;
int i;
rt_int32_t ret = RT_EOK;
i2c_info.clock = 50000; // 50kb/s
i2c_info.I2Cx = i2c_bus->u32Module;
i2c_init(&i2c_info);
for (i = 0; i < num; i++)
{
msg = &msgs[i];
if (msg->flags == RT_I2C_RD)
{
i2c_send_start_and_addr(&i2c_info, msg->addr, L2K_I2C_DIRECTION_READ);
i2c_receive_ack(&i2c_info);
i2c_receive_data(&i2c_info, (rt_uint8_t *)msg->buf, msg->len);
i2c_send_stop(&i2c_info);
}
else if(msg->flags == RT_I2C_WR)
{
i2c_send_start_and_addr(&i2c_info, msg->addr, L2K_I2C_DIRECTION_WRITE);
i2c_receive_ack(&i2c_info);
i2c_send_data(&i2c_info, (rt_uint8_t *)msg->buf, msg->len);
i2c_send_stop(&i2c_info);
}
ret++;
}
return ret;
}
在rt_i2c_master_xfer()函数中,
3. 根据i2c的传输协议需要写入对应的地址和msg内容,并引用新的语句,如 i2c_send_start_and_addr(&i2c_info, msg->addr, L2K_I2C_DIRECTION_READ);
代码如下, l2k_i2c_ret_t i2c_send_start_and_addr(l2k_i2c_info_t *i2c_info_p, unsigned char slave_addr, l2k_i2c_direction_t direction)
{
void *i2c_base = i2c_get_base(i2c_info_p->I2Cx);
unsigned char data = 0;
if (!i2c_poll_status(i2c_info_p, L2K_I2C_STATUS_BUSY))
return L2K_I2C_RET_TIMEOUT;
data = (slave_addr << 1) | ((L2K_I2C_DIRECTION_READ == direction) ? 1 : 0);
reg_write_8(data , i2c_base + L2K_I2C_DATA_OFFSET);
i2c_cmd_start(i2c_info_p);
if (!i2c_poll_status(i2c_info_p, L2K_I2C_STATUS_TIP))
return L2K_I2C_RET_TIMEOUT;
return L2K_I2C_RET_OK;
}
4. 这时就开始接触到硬件部分了 reg_write_8(data , i2c_base + L2K_I2C_DATA_OFFSET); 这句直接对内存赋值,赋值的地址是在头文件中选择的基址,如, #define I2C_BASE_ADDR 0xbfe01000 #define I2C0_OFF 0x0 //0x8 for I2C1, [11]bit of 1 #define I2C0_BASE CKSEG1ADDR(I2C0_BASE_ADDR + I2C0_OFF)
5. 所以,小结一下就是,首先要按照芯片的手册,获得主要的外围设备访问地址以及控制逻辑。基本上都是大力出奇迹,用直接读写地址和寄存器数据更新的方法。然后,按照RTT的struct 结构体要求,封装控制函数,通过注册函数关联起来。 6. 驱动编写完成,需要在ENV环境启动menuconfig先构建工程,使用i2c必须选择这个driver,否则在link的过程中显示无法找到 rt_i2c_bus_device_register(), 在配置好环境后,直接输入scons就生产二进制文件。 然后按照pmon管理的方式加载到内存,运行就可以显示结果了。 硬件框架 硬件主要包括龙芯L2K开发板,12V-10A直流电源,驱动风扇和电机,半导体制冷片。 调试引脚是RS232_UART_TX=59,RS232_UART_RX=60, 温度传感器引脚I2C1_SDA=3,I2C1_CLK=5, 冰箱开关控制 GPIO_07=7, 公用引脚是P3V3=1,GND=9, 硬件框架图如下。 引脚的编号图, 采用的主要元件, 温度传感器 组装的半导体制冷片, 元件的整体连接图,
软件框架说明 软件采用RTT实现主要线程管理,并且实现基本的闭环控制,并具备升级到智能节能方案。 方案框图如下,
流程图如下,
软件模块说明 软件部分包括2个并发的线程,第一个线程实现闭环的温度控制,第二个线程实现智能记录和节能管理。 使用RTT,就需要使用thread功能,先创建两个thread-entry,然后启动线程管理就可以了。进入RTT编程阶段就非常简单和逻辑清晰了。
演示效果使用scons编译通过, 复制文件到U盘G: 程序启动直接进入缺省的rtt文件,在SSD硬盘, 启动后按C键,进入PMON, 按照从u盘加载load之后运行的方式执行,进入smart_fridge的程序循环。
代码地址代码在BSP目录下,直接scons就可以了。
Fridge.rar
(1.62 MB)
文档:
RT-Thread应用创新设计大赛作品-智能冰箱.rar
(950.11 KB)
参考代码
主函数main()参考代码如下:int main(int argc, char** argv)
{
//rt_uint8_t temp[2] = {0, 0};
rt_thread_t tem_thread, eng_thread;
rt_uint8_t temp[6];
float *cur_temp;
rt_kprintf("Hi, this is Smart Fridge!\n");
int cnt;
cnt=10;
//fridge_init();
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(TEM_I2C_BUS_NAME);
if (i2c_bus == RT_NULL)
{
rt_kprintf("can't find %s device!\n", TEM_I2C_BUS_NAME);
}
else
{
write_reg(i2c_bus, TEM_NORMAL_CMD, temp);
rt_thread_mdelay(400);
temp[0] = 0x08;
temp[1] = 0x00;
write_reg(i2c_bus, TEM_CALIBRATION_CMD, temp);
rt_thread_mdelay(400);
initialized = RT_TRUE;
}
//cur_temp=cur_tmp_entry();
tem_thread = rt_thread_create("tem_trd",
cur_tmp_entry,
"temp_cur",
512,
RT_THREAD_PRIORITY_MAX / 2,
20);
if (tem_thread != RT_NULL)
{
rt_thread_startup(tem_thread);
}
eng_thread = rt_thread_create("eng_trd",
energy_saving_entry,
"eng_sav",
512,
RT_THREAD_PRIORITY_MAX / 2,
20);
if (eng_thread != RT_NULL)
{
rt_thread_startup(eng_thread);
}
return 0;
}
|