STM32F10x随笔(gcc+scons)

Posted 何亚红

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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/


































































































































































以上是关于STM32F10x随笔(gcc+scons)的主要内容,如果未能解决你的问题,请参考以下文章

STM32WB55 或 STM32F10 上的裸机编程

在 STM32f10x 中使用 EEPROM

为啥编译器标志“-mcpu=cortex-m3”与 stm32f10x 出错?

如何在 STM32F10x 上重定向 printf()?

stm32f10x 中断分组

stm32f10x 中断分组