1. 程式人生 > >STM32F10x隨筆(gcc+scons)

STM32F10x隨筆(gcc+scons)

isa 安裝過程 led https 模擬 波特率 extern 支持 自帶

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/


STM32F10x隨筆(gcc+scons)