感謝電子技術(shù)應(yīng)用的這次活動(dòng),申請(qǐng)到了一塊CH579的開(kāi)發(fā)板。
在芯片國(guó)產(chǎn)化的大趨勢(shì)下,很多人都在尋找可以用于替代進(jìn)口器件的國(guó)產(chǎn)芯片,所以趁此機(jī)會(huì),簡(jiǎn)單體驗(yàn)了一下沁恒芯片的開(kāi)發(fā),下面進(jìn)入正題。
首先來(lái)一張系統(tǒng)的照片
本次測(cè)試使用的開(kāi)發(fā)板型號(hào):CH579M-R1-1V2和維特智能的SHT30溫濕度傳感模塊。
開(kāi)發(fā)環(huán)境是MounRiver Studio V1.8.4。
以前沒(méi)用過(guò)這個(gè)開(kāi)發(fā)環(huán)境,但是感覺(jué)keil不是很好用,就體驗(yàn)一下,和CubeIDE 或者 RTthread studio差不多,用起來(lái)也很好上手,總之代碼補(bǔ)全什么的,好一些了,但是mounriver調(diào)試代碼的話(huà)好像要使用沁恒的調(diào)試器WCH-LINK,但是我沒(méi)有,所以想要用stlink什么的調(diào)試只能用keil了,不過(guò)代碼邏輯也不是太難,不太需要調(diào)試。
先新建CH579工程,點(diǎn)擊文件->新建MounRiver工程,查詢(xún)型號(hào)CH579,這里調(diào)試器類(lèi)型只有WCH-LINK,不能更改,但是我沒(méi)有。。。
新建好工程之后看看左側(cè)資源管理器,里邊就是你的工程文件了,其中主函數(shù)在scr文件夾下,一些已經(jīng)寫(xiě)好的外設(shè)驅(qū)動(dòng)函數(shù)在StdPeriphDriver文件夾下。本次我們需要用uart0來(lái)和SHT30傳感器通信,uart1用來(lái)適配letter-shell作為調(diào)試信息輸出。
刪掉自動(dòng)生成的工程里Main.c文件中的所用東西,開(kāi)始寫(xiě)自己的代碼。
先修改系統(tǒng)時(shí)鐘,直覺(jué)上驅(qū)動(dòng)文件里應(yīng)該有相關(guān)的時(shí)鐘配置函數(shù),那我去哪里找這個(gè)函數(shù)呢?
CH57x_clk.c文件里!找到一個(gè)函數(shù):
SetSysClock(CLK_SOURCE_PLL_40MHz); //重設(shè)系統(tǒng)時(shí)鐘,應(yīng)當(dāng)最先配置
main函數(shù)里直接調(diào)用,把系統(tǒng)時(shí)鐘設(shè)置為40M,根據(jù)注釋說(shuō)明,默認(rèn)會(huì)使用外部晶振。而且此處必須最先配置時(shí)鐘,不然先配置了串口之類(lèi)的外設(shè)時(shí)序會(huì)亂掉。
然后點(diǎn)一個(gè)LED燈指示,看驅(qū)動(dòng)文件里有
DelayMs()
這個(gè)函數(shù),但是這個(gè)函數(shù)是用循環(huán)nop這種方式寫(xiě)的,定時(shí)的精度em。。。1秒經(jīng)過(guò)編譯后成了1.5。自己寫(xiě)一個(gè)吧,簡(jiǎn)單使用systick。定義一個(gè)全局變量,然后在systick中斷函數(shù)中自減就行了。SysTick_Handler直接定義就好。
volatile uint32_t delay_cnt = 0;//需要加關(guān)鍵字修飾 不然會(huì)被優(yōu)化
void SysTick_Handler()
{
SysTick->CTRL &= ~(SysTick_CTRL_COUNTFLAG_Msk);//清除中斷標(biāo)志
delay_cnt--;
}
void user_delay_ms(uint32_t ms)//簡(jiǎn)單阻塞延時(shí),哪里需要哪里調(diào)
{
delay_cnt = ms;
while(delay_cnt);
}
然后在主函數(shù)中調(diào)用下邊的config函數(shù)設(shè)置時(shí)基(注意傳入的參數(shù)40000,因?yàn)榍斑呄到y(tǒng)主頻已經(jīng)改成了40M)
SysTick_Config(40000);//每過(guò)1ms進(jìn)入一次systick中斷
這樣就有了一個(gè)相對(duì)靠譜的延時(shí)函數(shù)了。
下一步控制GPIO9點(diǎn)燈(先把排針連上,那個(gè)LED排針還空著的),寫(xiě)個(gè)函數(shù)初始化GPIO,注意看清你用的GPIOA和GPIOB,就要調(diào)用相關(guān)的GPIOX_ModeCfg函數(shù),其他的外設(shè)也是如此,比如uart0和uart1就有不同的函數(shù)完成類(lèi)似的功能。
void LED_init()
{
GPIOB_SetBits(GPIO_Pin_19);
GPIOB_ModeCfg(GPIO_Pin_19, GPIO_ModeOut_PP_5mA);
}
指示燈能跑了,然后初始化串口。這里先初始化GPIO功能再初始化串口配置,串口需要打開(kāi)中斷。
這塊芯片本身支持硬件FIFO,但是在做數(shù)據(jù)解析時(shí)候我們自己還要定義一些自己的緩存,不過(guò)那是后邊的事情了。
串口1用于給移植的letter-shell做調(diào)試輸出使用,所以接收FIFO配配置1個(gè)字節(jié)就觸發(fā)中斷吧。
串口0用于和傳感器通信,配置了4字節(jié)觸發(fā)一次中斷。
始化完成了還不能用,看看下邊的串口中斷函數(shù)怎么寫(xiě)。
void user_uart_init(void)
{
GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
UART1_DefInit();//調(diào)試串口初始化
UART1_ByteTrigCfg(UART_1BYTE_TRIG);//設(shè)置FIFO 1字節(jié)觸發(fā)
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);//設(shè)置FIFO 4字節(jié)觸發(fā)
UART0_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );
NVIC_EnableIRQ( UART0_IRQn );
}
letter-shell是一個(gè)命令行調(diào)試工具,可以把你文件中定義的函數(shù)導(dǎo)出來(lái),通過(guò)命令行調(diào)用,裸機(jī)用來(lái)做調(diào)試工具還挺方便的。
另一個(gè)串口解析的代碼模塊是自己寫(xiě)的,可以解析挺多種特定格式的數(shù)據(jù)幀。
先看怎么移植letter-shell。
到git上把源碼下載下來(lái):GitHub - NevermindZZT/letter-shell: letter shell
然后參考了說(shuō)明開(kāi)始移植。
用戶(hù)裸機(jī)移植的話(huà),只需要實(shí)現(xiàn)write函數(shù),init函數(shù)和shellHandler的調(diào)用。
自己在mounriver的工程里創(chuàng)建一個(gè)letter-shell的文件夾,然后把你下載的源碼里src文件夾下所有文件都丟letter-shell文件夾里。然后在項(xiàng)目資源管理器里可以看見(jiàn)你復(fù)制過(guò)來(lái)的文件了,但是沒(méi)完事,還要右鍵letter-shell文件夾,把它加入工程的編譯。
還需要把你自己加進(jìn)來(lái)的這些文件的頭文件路徑添加好:
右鍵CH579M的工程,點(diǎn)擊屬性->C/C++構(gòu)建->設(shè)置->工具設(shè)置
我添加了letter-shell的文件夾,還有工程里src文件夾下還定義了一個(gè)user_def.h以及uda.h文件,路徑也要添加進(jìn)來(lái)。
移植說(shuō)明寫(xiě)了,如果用了GCC編譯器,還要修改什么字段,反正最后我們工程里obj文件夾的CH579.ld文件里加點(diǎn)代碼,找到40多行.text:這段,加上就好了。
源碼添加好了,然后letter-shell文件夾里創(chuàng)建一個(gè)shell_port.c和shell_port.h文件,自己實(shí)現(xiàn)的接口函數(shù)就保存在這里了。
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,還有實(shí)現(xiàn)的函數(shù),寫(xiě)函數(shù)直接調(diào)用的CH579給的驅(qū)動(dòng)函數(shù)就可以了。
讀數(shù)據(jù)我們?nèi)ゴ谥袛嗬飳?shí)現(xiàn),不需要定義讀函數(shù)了,所以這里只要定義這兩個(gè)函數(shù)就完事了,剩下什么線(xiàn)程啊,鎖啊之類(lèi)的,裸機(jī)我們用不到,就都刪除了。最后再到.h文件里把寫(xiě)的函數(shù)和變量聲明一下就可以了。
串口1的中斷處理函數(shù)直接定義:
void UART1_IRQHandler(void)
{
switch( UART1_GetITFlag() )//判斷中斷類(lèi)型
{
case UART_II_RECV_RDY:// 數(shù)據(jù)達(dá)到設(shè)置觸發(fā)點(diǎn)
while( R8_UART1_RFC ) //查看FIFO中剩余數(shù)據(jù)量
{
shellHandler(&shell,R8_UART1_RBR);//R8_UART1_RBR中的數(shù)據(jù)直接交給letter-shell
}
break;
case UART_II_RECV_TOUT: // 接收超時(shí),暫時(shí)一幀數(shù)據(jù)接收完成
while( R8_UART1_RFC )
{
shellHandler(&shell,R8_UART1_RBR);
}
break;
case UART_II_THR_EMPTY:
// 發(fā)送緩存區(qū)空,可繼續(xù)發(fā)送
break;
default:
break;
}
}
很簡(jiǎn)單,只是調(diào)用shellHandler把FIFO中的數(shù)據(jù)接收就行了。使用之前在主函數(shù)里調(diào)用一下剛才你寫(xiě)的shellinit初始化哪個(gè)函數(shù)就可以了。
然后 串口解析,在工程里添加了自己寫(xiě)的uda模塊,用的時(shí)候就三個(gè)函數(shù),大概思路是先定義一幀數(shù)據(jù)幀頭啊長(zhǎng)度啊之類(lèi)的東西,在使用功能之前初始化一次。然后在串口0接收中斷里拿到數(shù)據(jù),收進(jìn)自己的解析緩存區(qū),在主whiel中循環(huán)解析就可以了,每次解析一幀數(shù)據(jù)出來(lái)。篇幅限制就不再展開(kāi)說(shuō)了。
最后主函數(shù)長(zhǎng)這樣:
還有個(gè)shell導(dǎo)出命令測(cè)試的函數(shù):
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);
主要完成了在主函數(shù)中解析到SHT30的溫度和濕度數(shù)據(jù),當(dāng)外部指令通過(guò)shell調(diào)用函數(shù)時(shí)候,將此時(shí)的系統(tǒng)時(shí)間tick值和溫濕度數(shù)據(jù)打印出來(lái)。
最后編譯,并且通過(guò)WCHISPStudio工具下載,下載代碼之前要按住開(kāi)發(fā)板上的DOWNLOAD按鍵,再打開(kāi)開(kāi)關(guān),這時(shí)候下載工具才找得到設(shè)備。
調(diào)試效果如下圖:
總體來(lái)說(shuō),體驗(yàn)還可以,有機(jī)會(huì)試下其他的芯片。本測(cè)試的代碼附在后邊,需要自取。
https://gitee.com/Echo365/open.git