STM32F10x隨筆(gcc+scons)
by HYH | 2017 年 9 月 13 日 下午 10:22
一.開發板簡介
開發板上除了必須的晶振和復位電路,只有一個接在PB12上的led(用於測試)。
二.串口下載(stm32flash)
1.下載及安裝。
官方下載:https://sourceforge.net/projects/stm32flash/files/(windows下載:stm32flash-0.5-win64.zip ,Linux下載:stm32flash-0.5.tar.gz)
下載地址:stm32flash-0.5.tar
32位 windows版下載地址:stm32flash-win32
解壓之後,直接進入文件夾
make
make install
2.下載前的硬件設置.
1)除電源和地外,將USB轉串口的RX接到PA9,TX接到PA10.
2)Boot0接高電平(正常運行時接低電平),Boot1接低電平。
3)連接完成後,按復位鍵。
3.常用命令(需root權限)
備份stm32的flash到文件:
stm32flash -r 備份文件名 串口設備名
從文件燒寫stm32flash的flash:
stm32flash -w 文件名 串口設備名
4.附加:
由於rtthread的組件finsh組件,改變硬件連接(Boot0接低電平)並按復位鍵之後,可使用燒寫串口調試(直接調用導出給finsh組件的C函數),無需更改串口連接。
調試波特率:115200
5.附加2:
在windows下,串口設備的表示方式為:COMn(n為整數,有時會失敗)或 \\.\COMn(n為整數且兼容性更好)
其中n的值由通過設備管理器人工確定。
putty也可在windows下使用:
stm32flash在windows下使用時,需要將stm32flash.exe所在文件夾添加到PATH變量中,或者將stm32flash.exe放到PATH變量中有的目錄(C:\windows)當中。運行stm32flash時最好使用管理員權限。如果打開串口失敗,檢查是否有其它軟件(如putty)在使用該串口。
三.RT-thread編譯以及簡單使用。
1.編譯環境搭建(arm-none-eabi-gcc+scons)
1)軟件下載:
gcc:https://launchpad.net/gcc-arm-embedded/+download
python:https://www.python.org/downloads/
scons:http://scons.org/pages/download.html
可選軟件:notepad++
2)安裝註意事項:
gcc
安裝過程中記好安裝路徑:
選擇添加PATH變量
python
安裝過程中記好安裝路徑:
選擇添加PATH變量:
scons
必選先成功安裝python.
安裝完成後,手動將 python目錄\scripts 添加到PATH變量。
如果能夠運行scons即編譯環境搭建完成:
2.rt-thread下載及編譯前設置
1)下載
下載:http://www.rt-thread.org/page/31.html
http://pan.baidu.com/s/1mgIAyWo
將下載的zip包解壓。
2)編譯需要用到目錄及文件簡介(非開發)
examples\:rt-thread的示例。
bsp\:此目錄下有rt-thread對各種芯片的支持,根據實際需要進入相應目錄運行scons編譯。如bsp\stm32f10x就是對stm32f103c6t8的支持,下面是對該目錄的介紹:
rtconfig.py:編譯工具設置,編譯前必須設置好此文件。
rtconfig.h:rt-thread配置。通過註釋取消註釋來配置rt-thread。其中含有USING字樣的為可選組件,可進行裁剪(組件間似乎有依賴關系,修改前最好備份)。
drivers:驅動文件夾,有的文件需要根據芯片實際情況或電路板外圍電路情況進行修改。
applications:應用文件夾.其中startup.c為main函數所在文件。application.c包含一個led的任務,其調用的drivers\led.c中的函數控制一個led燈(用於顯示正在運行)。
3)文件配置
rtconfig.py
CROSS_TOOL=’gcc’
EXEC_PATH = ‘gcc安裝目錄/bin‘(將路徑中的\改為/)
drivers\board.h
#define STM32_SRAM_SIZE 實際內存大小
drivers\led.c
將led1改為PB12(所用開發板在PB12腳連了一個led)
#define led1_rcc RCC_APB2Periph_GPIOB
#define led1_gpio GPIOB
#define led1_pin (GPIO_Pin_12)
4)編譯
scons 編譯
scons -c 清理編譯文件
5)燒錄
rtthread.bin為燒錄所用文件。
6)附加:
這樣編譯完成的rt-thread把唯一的一個led給占用了。為了釋放這個led,可註釋掉
rt_hw_led_init();
rt_hw_led_on(0);
rt_hw_led_off(0);
這樣編譯的固件,就不會占用led了。
就可使用drivers\led.c導出的led函數控制led了。(不存在led2,只有led1)。
led函數用法:
led(led選擇,電平); led選擇為1是選擇led2,0選擇led1.
7).附加2:
finsh簡單使用:
list(); 查看已經導出的函數
list_thread(); 查看任務
list_timer();查看定時器
3.RT-thread簡單開發
1)添加.c文件並使用FINSH打印字符
有時可能需要自己添加一個.c文件(以在applications\下添加一個app.c為例)。
首先新建一個名為app.c的文本文件。
編輯同目錄的SConscript文件,將app.c添加到文件列的頂端。
接下來就是寫app.c的內容
因為是用rt-thread開發應用,因此應當包含rtthread.h.
因為FINSH是可選組件,因此要在
#ifdef RT_USING_FINSH
#include <finsh.h>
和
#endif
之間寫程序
然後就是寫要導出的函數的主體了
其中rt_kprintf();可用於打印字符,用法跟printf()類似。
最後導出函數
FINSH_FUNCTION_EXPORT(函數名,描述)
註意:由於是宏定義,無須分號。
接下來就是編譯
結果:
2)利用pin設備控制GPIO
要使用pin設備必須包含<drivers\pin.h>。
pin設備的引腳號數在stm32f10x下是由低層驅動(drivers\gpio.c)控制的.
如 __STM32_PIN(30, APB2, A, 5),表示30號引腳為PA5。
電平表示方式:
輸入輸出模式表示方式:
pin設備結構體:
pin輸入輸出模式結構體:
pin狀態結構體:
常用函數:
rt_device_find(“pin”);//獲取pin設備指針,返回值為rt_device_t,必須強制轉換成rt_device_pin的指針.
rt_device_open(rt_device_pin的指針->parent,RT_NULL);//打開pin設備
rt_device_control(rt_device_pin的指針->parent,RT_NULL,pin輸入輸出模式結構體的指針);//應用輸入輸出模式
rt_device_write(rt_device_pin的指針->parent,RT_NULL,pin狀態結構體的指針,sizeof(pin狀態結構體));//寫pin
rt_device_read(rt_device_pin的指針->parent,RT_NULL,pin狀態結構體的指針,sizeof(pin狀態結構體));//讀pin
示例:
app
結果:
4.STM32F10X簡單開發
rt-thread自帶stm32f10x標準庫,不依賴rt-thread.
目錄:Libraries\STM32F10x_StdPeriph_Driver
1)GPIO口
GPIO口表示方式:GPIOA GPIOB … GPIOG (實際情況視芯片而定,對應的IO時鐘為RCC_APB2Periph_GPIOA,RCC_APB2Periph_GPIOB … RCC_APB2Periph_GPIOG)
針腳表示方式:GPIO_PIN_n(n為數字)
如PA0的GPIO口為GPIOA,針腳為GPIO_PIN_0,IO時鐘為RCC_APB2Periph_GPIOA。
要使用GPIO庫函數,必須先包含stm32f10x_gpio.h
下面介紹幾個常用的函數:
GPIO_ReadInputDataBit(GPIO口,針腳);
讀取特定GPIO口的特定針腳。
GPIO_ReadInputData(GPIO口);
讀取GPIO口
GPIO_ResetBits(GPIO口,針腳);
設置某個針腳為0
GPIO_SetBits(GPIO口,針腳);
設置某個針腳為1
下面是初始化示例代碼:
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(IO時鐘,ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO模式;
GPIO_InitStructure.GPIO_Speed = GPIO速度;
GPIO_InitStructure.GPIO_Pin = 針腳;
GPIO_Init(GPIO口, &GPIO_InitStructure);
其中GPIO口,針腳,資源名可通過|號一次初始多個(如GPIO_PIN0|GPIO_PIN_1表示兩個都初始化)
GPIO模式:
GPIO速度:
示例:
app
結果:
PA1接PA0(後三個命令)
用萬用表測試PA1的電平變化也正確。
2)ADC示例(ADC1通道5)
app
代碼:
#include <rtthread.h>
#include “stm32f10x_adc.h”
#ifdef RT_USING_FINSH
#include <finsh.h>
void ADC_Config(void) //ADC初始化
{
ADC_InitTypeDef ADC_1; //初始化結構體聲明
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //開啟ADC1時鐘,ADC2為RCC_APB2Periph_ADC1
ADC_StructInit(&ADC_1);//導入默認配置:獨立模式,不掃描,不連續轉換,TIM1_CC1事件觸發,右對齊,通道數1
ADC_1.ADC_ScanConvMode = ENABLE;//開啟掃描模式
ADC_1.ADC_ContinuousConvMode = ENABLE;//開啟連續掃描模式
ADC_1.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//軟件觸發
ADC_Init(ADC1,&ADC_1); //初始化ADC1
ADC_RegularChannelConfig(ADC1,ADC_Channel_5,1,ADC_SampleTime_55Cycles5);//通道組,設置第五個通道的順序為1,采樣時間55.5周期
}
void GPIO_Config()//GPIO初始化,主要把ADC輸入通道所在引腳配置為模擬輸入模式
{
GPIO_InitTypeDef PA5;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//開啟GPIOA時鐘
PA5.GPIO_Pin=GPIO_Pin_5;//第五引腳
PA5.GPIO_Mode=GPIO_Mode_AIN;//模擬輸入模式
GPIO_Init(GPIOA,&PA5);//初始化PA5
}
void adctest()
{
uint16_t ad_v=0;//保存轉換結果的變量
GPIO_Config();//調用GPIO初始化
ADC_Config();//調用ADC初始化
ADC_Cmd(ADC1,ENABLE);//開啟ADC1
ADC_ResetCalibration(ADC1);//重置ADC1校準寄存器
while(ADC_GetResetCalibrationStatus(ADC1));//等待重置完成
ADC_StartCalibration(ADC1);//開始校準ADC1
while(ADC_GetCalibrationStatus(ADC1));//等待校準完成
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//開始軟件觸發
rt_kprintf(“start adc1(Channel_5)\n”);//調用finsh組件
while(1)
{
rt_thread_delay( RT_TICK_PER_SECOND/2 ); /* 睡眠0.5秒(RTthread功能) */
ad_v=ADC_GetConversionValue(ADC1);//獲取最近的轉換值
ad_v&=0x0fff;//保留右對齊的12位
rt_kprintf(“ADC1:%d\n”,ad_v);//調用finsh打印結果
}
}
FINSH_FUNCTION_EXPORT(adctest,test adc (pa5))
#endif
包含文件:stm32f10x_adc.h
通道號數由引腳第二功能決定(如PA5的第二功能是ADC_IN5,所以它是通道5)
示例結果:
3)UASRT(UART2查詢方式)
app
#include <rtthread.h>
//#include <rtdevice.h>
//#include <drivers\serial.h>
#include <stm32f10x_usart.h>
#ifdef RT_USING_FINSH
#include <finsh.h>
void uartinit()//初始化GPIO,UART2
{
RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE);//打開UART2時鐘
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA |RCC_APB2Periph_AFIO,ENABLE);//打開GPIOA(UART2所在GPIO口)時鐘
GPIO_InitTypeDef GPIO_InitStructure;//GPIO初始化結構體
/*設置USART2 Tx(PA.2)*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*設置USART2 Rx(PA.3) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;//UASRT初始化結構體
USART_InitStructure.USART_BaudRate = 9600; //波特率9600
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//數據位8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
USART_InitStructure.USART_Parity = USART_Parity_No; //無校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不開啟流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //收發模式
USART_Init(USART2, &USART_InitStructure);//初始化UASRT2
USART_Cmd(USART2, ENABLE); //啟動UASRT2
}
void testuart()
{
uartinit();
while(1)
{
USART2->SR;//防止第一個字符被忽略
while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);//等待接收完成
USART_SendData(USART2, USART_ReceiveData(USART2));//發送接收的數據
while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//等待發送完成
}
}
FINSH_FUNCTION_EXPORT(testuart,uart2 test)
#endif
必須包含頭文件:stm32f10x_usart.h
實驗結果:
4)i2c(I2C1)
i2c的從硬件是PCF8591(8位AD/DA轉換器)
app
#include <rtthread.h>
#include “stm32f10x_adc.h”
#ifdef RT_USING_FINSH
#include <finsh.h>
void i2c_init()//i2c初始化設置
{
I2C_InitTypeDef I2C_Struct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 , ENABLE);
I2C_StructInit(&I2C_Struct);
I2C_Struct.I2C_ClockSpeed=100000;
I2C_Struct.I2C_Mode=I2C_Mode_I2C ;
I2C_Struct.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_Struct.I2C_Ack=I2C_Ack_Enable; //使能應答位
I2C_Struct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit ;
I2C_Init( I2C1, &I2C_Struct);
I2C_Cmd(I2C1,ENABLE);
}
void gpio_init()//設置i2c引腳
{
GPIO_InitTypeDef GPIO_Struct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOB,ENABLE);
GPIO_Struct.GPIO_Pin=GPIO_Pin_6|GPIO_Pin_7;
GPIO_Struct.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_Struct.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Struct);
}
void I2C_WaitEepromStandbyState(u8 id) //防守函數,非常重要
{
vu16 SR1_Tmp = 0;
do
{
/* Send START condition */
I2C_GenerateSTART(I2C1, ENABLE);
/* Read I2C1 SR1 register */
SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C1, id, I2C_Direction_Transmitter);
}while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002)); //estimate the value of ADDR
/* Clear AF flag */
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
}
void i2c_write(u8 id,u8 address,u8 byte)//I2C寫函數,參數依次是id,地址,要寫入的值。(均是8位)
{
I2C_WaitEepromStandbyState(id);
I2C_GenerateSTART ( I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //ev5
I2C_Send7bitAddress ( I2C1,id, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED )); //ev6
I2C_SendData ( I2C1, address);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING)); //ev8
I2C_SendData ( I2C1, byte);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //ev8_2
I2C_GenerateSTOP(I2C1, ENABLE);
}
u8 i2c_read(u8 id,u8 address)//i2c讀函數,參數依次是id,地址。(均是8位)
{
u8 temp;
I2C_WaitEepromStandbyState(id);
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_AcknowledgeConfig(I2C1, DISABLE); //
I2C_Send7bitAddress ( I2C1,id, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData ( I2C1, address);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING));
I2C_GenerateSTART(I2C1, ENABLE); //restart
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //ev5
I2C_Send7bitAddress ( I2C1,id, I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //ev6
I2C_GenerateSTOP(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); //ev7
temp=I2C_ReceiveData(I2C1);
return temp;
}
int testi2c(void)
{
u8 data[5];//定義數組用於儲存8位AD結果
gpio_init();
rt_kprintf(“GPIO init\n”);
i2c_init();
rt_kprintf(“start i2c\n”);
while(1)//讀取pcf8591,A0=A1=A2=0
{
rt_thread_delay( RT_TICK_PER_SECOND/10 ); //睡眠0.1秒,讀取過快可能引起死機.
data[0]=i2c_read(0x91,0x40);//讀取通道0
data[1]=i2c_read(0x91,0x41);//讀取通道1
data[2]=i2c_read(0x91,0x42);//讀取通道2
data[3]=i2c_read(0x91,0x43);//讀取通道3
data[4]=data[0];
i2c_write(0x90,0x40,data[4]);//DA轉換
rt_kprintf(“\radc:%d %d %d %d\t\t”,data[0],data[1],data[2],data[3]);//打印AD結果
}
}
FINSH_FUNCTION_EXPORT(testi2c,test i2c)
#endif
必須包含的頭文件:stm32f10x_adc.h
結果:
https://hyhsystem.cn/wordpress/
Copyright ?2018 何亞紅的博客 unless otherwise noted.
STM32F10x隨筆(gcc+scons)