Echo365

基于CH579的SHT30模块温湿度数据串口解析和letter-shell移植

1
阅读(576)

感谢电子技术应用的这次活动,申请到了一块CH579的开发板。

在芯片国产化的大趋势下,很多人都在寻找可以用于替代进口器件的国产芯片,所以趁此机会,简单体验了一下沁恒芯片的开发,下面进入正题。

首先来一张系统的照片

2.jpg














本次测试使用的开发板型号:CH579M-R1-1V2和维特智能的SHT30温湿度传感模块。

开发环境是MounRiver Studio V1.8.4。

以前没用过这个开发环境,但是感觉keil不是很好用,就体验一下,和CubeIDE 或者 RTthread studio差不多,用起来也很好上手,总之代码补全什么的,好一些了,但是mounriver调试代码的话好像要使用沁恒的调试器WCH-LINK,但是我没有,所以想要用stlink什么的调试只能用keil了,不过代码逻辑也不是太难,不太需要调试。

先新建CH579工程,点击文件->新建MounRiver工程,查询型号CH579,这里调试器类型只有WCH-LINK,不能更改,但是我没有。。。

image.png

新建好工程之后看看左侧资源管理器,里边就是你的工程文件了,其中主函数在scr文件夹下,一些已经写好的外设驱动函数在StdPeriphDriver文件夹下。本次我们需要用uart0来和SHT30传感器通信,uart1用来适配letter-shell作为调试信息输出。


image.png


删掉自动生成的工程里Main.c文件中的所用东西,开始写自己的代码。

先修改系统时钟,直觉上驱动文件里应该有相关的时钟配置函数,那我去哪里找这个函数呢?

CH57x_clk.c文件里!找到一个函数:

SetSysClock(CLK_SOURCE_PLL_40MHz); //重设系统时钟,应当最先配置

main函数里直接调用,把系统时钟设置为40M,根据注释说明,默认会使用外部晶振。而且此处必须最先配置时钟,不然先配置了串口之类的外设时序会乱掉。

然后点一个LED灯指示,看驱动文件里有

DelayMs()

这个函数,但是这个函数是用循环nop这种方式写的,定时的精度em。。。1秒经过编译后成了1.5。自己写一个吧,简单使用systick。定义一个全局变量,然后在systick中断函数中自减就行了。SysTick_Handler直接定义就好。

volatile uint32_t delay_cnt = 0;//需要加关键字修饰 不然会被优化
void SysTick_Handler()
{    
    SysTick->CTRL &= ~(SysTick_CTRL_COUNTFLAG_Msk);//清除中断标志    
    delay_cnt--;
}
void user_delay_ms(uint32_t ms)//简单阻塞延时,哪里需要哪里调
{    
    delay_cnt = ms;    
    while(delay_cnt);
}

然后在主函数中调用下边的config函数设置时基(注意传入的参数40000,因为前边系统主频已经改成了40M)

SysTick_Config(40000);//每过1ms进入一次systick中断

这样就有了一个相对靠谱的延时函数了。

下一步控制GPIO9点灯(先把排针连上,那个LED排针还空着的),写个函数初始化GPIO,注意看清你用的GPIOA和GPIOB,就要调用相关的GPIOX_ModeCfg函数,其他的外设也是如此,比如uart0和uart1就有不同的函数完成类似的功能。

void LED_init()
{    
    GPIOB_SetBits(GPIO_Pin_19);    
    GPIOB_ModeCfg(GPIO_Pin_19, GPIO_ModeOut_PP_5mA);
}

指示灯能跑了,然后初始化串口。这里先初始化GPIO功能再初始化串口配置,串口需要打开中断。

这块芯片本身支持硬件FIFO,但是在做数据解析时候我们自己还要定义一些自己的缓存,不过那是后边的事情了。

串口1用于给移植的letter-shell做调试输出使用,所以接收FIFO配配置1个字节就触发中断吧。

串口0用于和传感器通信,配置了4字节触发一次中断。

始化完成了还不能用,看看下边的串口中断函数怎么写。

void user_uart_init(void)
{    
    GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);   
    GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);    
    UART1_DefInit();//调试串口初始化   
    UART1_ByteTrigCfg(UART_1BYTE_TRIG);//设置FIFO 1字节触发   
    UART1_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT ); 
    NVIC_EnableIRQ( UART1_IRQn );  
    
    GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU);  
    GPIOB_ModeCfg(GPIO_Pin_7, GPIO_ModeOut_PP_5mA); 
    UART0_DefInit();//通信串口初始化    
    UART0_ByteTrigCfg(UART_4BYTE_TRIG);//设置FIFO 4字节触发
    UART0_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );  
    NVIC_EnableIRQ( UART0_IRQn );
}

letter-shell是一个命令行调试工具,可以把你文件中定义的函数导出来,通过命令行调用,裸机用来做调试工具还挺方便的。

另一个串口解析的代码模块是自己写的,可以解析挺多种特定格式的数据帧。

先看怎么移植letter-shell。

到git上把源码下载下来:GitHub - NevermindZZT/letter-shell: letter shell

然后参考了说明开始移植。

用户裸机移植的话,只需要实现write函数,init函数和shellHandler的调用。

自己在mounriver的工程里创建一个letter-shell的文件夹,然后把你下载的源码里src文件夹下所有文件都丢letter-shell文件夹里。然后在项目资源管理器里可以看见你复制过来的文件了,但是没完事,还要右键letter-shell文件夹,把它加入工程

的编译。


image.png



















还需要把你自己加进来的这些文件的头文件路径添加好:

右键CH579M的工程,点击属性->C/C++构建->设置->工具设置

我添加了letter-shell的文件夹,还有工程里src文件夹下还定义了一个user_def.h以及uda.h文件,路径也要添加进来。

image.png


移植说明写了,如果用了GCC编译器,还要修改什么字段,反正最后我们工程里obj文件夹的CH579.ld文件里加点代码,找到40多行.text:这段,加上就好了。

image.png


源码添加好了,然后letter-shell文件夹里创建一个shell_port.c和shell_port.h文件,自己实现的接口函数就保存在这里了。

Shell shell;
char shellBuffer[512];

short userShellWrite(char *data, unsigned short len)
{	
    UART1_SendString((uint8_t *)data,len);    
    return len;
}
void userShellInit(void)
{    
    shell.write = userShellWrite;  
    shellInit(&shell, shellBuffer, 512);
 }

这里定义了一些模块需要的变量和buff,还有实现的函数,写函数直接调用的CH579给的驱动函数就可以了。

读数据我们去串口中断里实现,不需要定义读函数了,所以这里只要定义这两个函数就完事了,剩下什么线程啊,锁啊之类的,裸机我们用不到,就都删除了。最后再到.h文件里把写的函数和变量声明一下就可以了。

串口1的中断处理函数直接定义:

void UART1_IRQHandler(void)
{   
     switch( UART1_GetITFlag() )//判断中断类型    
     {
         case UART_II_RECV_RDY:// 数据达到设置触发点    
             while( R8_UART1_RFC ) //查看FIFO中剩余数据量  
             {               
                 shellHandler(&shell,R8_UART1_RBR);//R8_UART1_RBR中的数据直接交给letter-shell         
             }
             break;
         case UART_II_RECV_TOUT: // 接收超时,暂时一帧数据接收完成           
             while( R8_UART1_RFC )            
             {               
                 shellHandler(&shell,R8_UART1_RBR);            
             }          
          break;       
         case UART_II_THR_EMPTY:         
         // 发送缓存区空,可继续发送            
         break;        
         default:            
         break;    
     }
}

很简单,只是调用shellHandler把FIFO中的数据接收就行了。使用之前在主函数里调用一下刚才你写的shellinit初始化哪个函数就可以了。

然后 串口解析,在工程里添加了自己写的uda模块,用的时候就三个函数,大概思路是先定义一帧数据帧头啊长度啊之类的东西,在使用功能之前初始化一次。然后在串口0接收中断里拿到数据,收进自己的解析缓存区,在主whiel中循环解析就可以了,每次解析一帧数据出来。篇幅限制就不再展开说了。

最后主函数长这样:


image.png



















还有个shell导出命令测试的函数:

void print_current_temp_hum(void)
{    
    uint32_t tick_ = SYS_GetSysTickCnt();   
    printf("current tick = %d\r\n",tick_); 
    printf("mounriver >> temp = %.2f  humi = %.2f\r\n",temp_val, humi_val);
}SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_MAIN), my_print_cmd, print_current_temp_hum, this is a print test);

主要完成了在主函数中解析到SHT30的温度和湿度数据,当外部指令通过shell调用函数时候,将此时的系统时间tick值和温湿度数据打印出来。

最后编译,并且通过WCHISPStudio工具下载,下载代码之前要按住开发板上的DOWNLOAD按键,再打开开关,这时候下载工具才找得到设备。


3.png



调试效果如下图:


image.png


总体来说,体验还可以,有机会试下其他的芯片。本测试的代码附在后边,需要自取。

https://gitee.com/Echo365/open.git