一env使用
1.1:使用env构建rtthread项目:安装env_released_1.1.1,打开env命令终端可以对rtthread的项目进行配置,在env中使用menuconfig命令进行图形界面的选择配置,menuconfig除了系统自带的根目录,外设的设备添加选项由项目的kconfig配置得到,将kconfig添加想添加的外设,使用menuconfig配置后,保存,会把相应的配置更新到.config文件中,然后使用命令scons --target=mdk5(scons --target=xxx)更新到项目工程中。
1.2:打开项目工程,在rtconfig.h中可以得到配置好的头文件包含了,相应的rtthread中的外设支持头文件,源文件可以在rtdevice.h中查看,相应的源文件,头文件加入工程需要在rtconfi.h配置相应的头文件。Menuconfig结合kconfig可以完成这一步。
1.3:当系统是不能支持env的,也就是项目工程不需要SConscript脚本配置的情况下使用手动添加即可。(另项目工程的各目录下的配置脚本是各目录下的SConscript脚本,开发时候可以在相应的脚本中添加用户需要的配置)其中env工具要求项目工程必须有bsp目录(即板级支持包,里面是不同芯片板卡系列的实现工程)。其执行添加在项目工程所在目录下,查找bsp。
2:工程更改基本配置:
2.1:kconfig,即rtconfig 的头文件配置就可以直接加载3步骤的东西
2.2:其中基本要配置的script有 :E:\rtt\rt-thread-master - 1\bsp\gd32303e-eval\drivers这个目录下的script是向工程自动添加上相应为外设源文件(用户添加的)
E:\rtt\rt-thread-master - 1\bsp\gd32303e-eval\Libraries 下的SConscript是配置工程的开发环境的脚本,比如更改hd.s等
2.3:然后rtthread提供的外设结构体在E:\rtt\rt-thread-master - 1\components\drivers下
二相应的添加外设操作(env):
三用户完成外设驱动层(最主要)
1:Rtthread是一个分层架构的系统,用户需要对接rtthread外设并将其提供的ops结构体进行实现,之后就可以开始使用和驱动外设了,
实现这个ops的源代码可以参考各芯片系列的。其中主要的原因就是各外设需要挂载到总线上,调用注册函数就进行挂载,外部即可使用。
2:其中ops基本的结构会包含init,control,write,read这几部分,需要相应的去实现,二各外设基本分成两部分:1是类似spi这种有总线的外设,其最大的区别就是可以在一个spi总线上链接很多的设备,各设备的驱动芯片不同,导致需要添加不同的芯片驱动,2是类似串口,定时器这种的,基本都是开始初始化一个设备就可以进行使用。所以两者之间的ops结构体实现的方式是有些不同的。
Spi例子讲解:
#include "drv_spi.h"
#include "gd32f30x.h"
#include <rtthread.h>
/* private rt-thread spi ops function */
static rt_err_t configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration);
static rt_uint32_t xfer(struct rt_spi_device* device, struct rt_spi_message* message);
static struct rt_spi_ops gd32_spi_ops = //实现rt_spi_ops接口,主要实现里面的成员属性
{
configure,
xfer
};
//主要用于配置spi
static rt_err_t configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration)
{
spi_parameter_struct spi_init_struct;
rt_uint32_t spi_periph = (rt_uint32_t)device->bus->parent.user_data;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
if(configuration->data_width <= 8)
{
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
}
else if(configuration->data_width <= 16)
{
spi_init_struct.frame_size = SPI_FRAMESIZE_16BIT;
}
else
{
return RT_EIO;
}
{
rcu_clock_freq_enum spi_src;
rt_uint32_t spi_apb_clock;
rt_uint32_t max_hz;
max_hz = configuration->max_hz;
if (spi_periph == SPI1 || spi_periph == SPI2)
{
spi_src = CK_APB1; //×î´ó60M
}
else
{
spi_src = CK_APB2;
}
spi_apb_clock = rcu_clock_freq_get(spi_src);
if(max_hz >= spi_apb_clock/2) //2·ÖƵ
{
spi_init_struct.prescale = SPI_PSC_2;
}
else if (max_hz >= spi_apb_clock/4)
{
spi_init_struct.prescale = SPI_PSC_4;
}
else if (max_hz >= spi_apb_clock/8)
{
spi_init_struct.prescale = SPI_PSC_8;
}
else if (max_hz >= spi_apb_clock/16)
{
spi_init_struct.prescale = SPI_PSC_16;
}
else if (max_hz >= spi_apb_clock/32)
{
spi_init_struct.prescale = SPI_PSC_32;
}
else if (max_hz >= spi_apb_clock/64)
{
spi_init_struct.prescale = SPI_PSC_64;
}
else if (max_hz >= spi_apb_clock/128)
{
spi_init_struct.prescale = SPI_PSC_128;
}
else
{
/* min prescaler 256 */
spi_init_struct.prescale = SPI_PSC_256;
}
} /* baudrate */
switch(configuration->mode & RT_SPI_MODE_3)
{
case RT_SPI_MODE_0:
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
break;
case RT_SPI_MODE_1:
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
break;
case RT_SPI_MODE_2:
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE;
break;
case RT_SPI_MODE_3:
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
break;
}
/* MSB or LSB */
if(configuration->mode & RT_SPI_MSB)
{
spi_init_struct.endian = SPI_ENDIAN_MSB; //zege
}
else
{
spi_init_struct.endian = SPI_ENDIAN_LSB;
}
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init(spi_periph, &spi_init_struct);
spi_crc_off(spi_periph); //spi_crc_polynomial_set(spi_periph,7); spi_crc_on(spi_periph);
spi_enable(spi_periph);
return RT_EOK;
};
//实现发送接受接口
static rt_uint32_t xfer(struct rt_spi_device* device, struct rt_spi_message* message)
{
rt_base_t gd32_cs_pin = (rt_base_t)device->parent.user_data;
rt_uint32_t spi_periph = (rt_uint32_t)device->bus->parent.user_data;
struct rt_spi_configuration * config = &device->config;
RT_ASSERT(device != NULL);
RT_ASSERT(message != NULL);
/* take CS */
if(message->cs_take)
{
gpio_bit_reset(GPIOA,GPIO_PIN_4);
}
{
if(config->data_width <= 8)
{
const rt_uint8_t * send_ptr = message->send_buf;
rt_uint8_t * recv_ptr = message->recv_buf;
rt_uint32_t size = message->length;
DEBUG_PRINTF("spi poll transfer start: %d\n", size);
while(size--)
{
rt_uint8_t data = 0xFF;
if(send_ptr != RT_NULL)
{
data = *send_ptr++;
}
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE));
spi_i2s_data_transmit(spi_periph, data);
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE));
data = spi_i2s_data_receive(spi_periph);
if(recv_ptr != RT_NULL)
{
*recv_ptr++ = data;
}
}
}
else if(config->data_width <= 16)
{
const rt_uint16_t * send_ptr = message->send_buf;
rt_uint16_t * recv_ptr = message->recv_buf;
rt_uint32_t size = message->length;
while(size--)
{
rt_uint16_t data = 0xFF;
if(send_ptr != RT_NULL)
{
data = *send_ptr++;
}
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_TBE));
spi_i2s_data_transmit(spi_periph, data);
while(RESET == spi_i2s_flag_get(spi_periph, SPI_FLAG_RBNE));
data = spi_i2s_data_receive(spi_periph);
if(recv_ptr != RT_NULL)
{
*recv_ptr++ = data;
}
}
}
}
/* release CS */
if(message->cs_release)
{
gpio_bit_set(GPIOA,GPIO_PIN_4);
}
return message->length;
};
int gd32_hw_spi_init(void)
{
int result = 0;
#ifdef RT_USING_SPI0
static struct rt_spi_bus spi_bus0;
spi_bus0.parent.user_data = (void *)SPI0;
result = rt_spi_bus_register(&spi_bus0, "spi0", &gd32_spi_ops);
rcu_periph_clock_enable(RCU_GPIOA|RCU_AF);
rcu_periph_clock_enable(RCU_SPI0);
/* SPI0_SCK(PA5), SPI0_MISO(PA6) and SPI0_MOSI(PA7) GPIO pin configuration */
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
#endif
return result;
}
INIT_BOARD_EXPORT(gd32_hw_spi_init);
#endif
以上代码用于向系统注册spi总线,方便外部挂载设备
外部挂载设备可以如下调用:
#define SPI_BUS_NAME "spi0" //设备挂载的总线名称
#define SPI_dac104s085_DEVICE_NAME "spi01" //随便命名
#define CS_PIN 29 //根据不同芯片的引脚号去选择
struct stm32_hw_spi_cs
{
rt_uint32_t pin;
};
static struct rt_spi_device spi_dev_dac104s085;
static struct stm32_hw_spi_cs spi_cs;
rt_err_t dac104s085_write_data(const rt_uint16_t *data,const int lendth)
{
rt_size_t len;
len = rt_spi_send(&spi_dev_dac104s085, data, lendth);
if (len != lendth)
{
OLED_TRACE("dac104s085_write_data error. %d\r\n",len);
return -RT_ERROR;
}
else
{
return RT_EOK;
}
}
static int rt_hw_dac104s085_config(void)
{
rt_err_t res;
spi_cs.pin = CS_PIN;
rt_pin_mode(spi_cs.pin, GPIO_MODE_OUT_PP);
rt_pin_write(CS_PIN, PIN_HIGH);
res = rt_spi_bus_attach_device(&spi_dev_dac104s085, SPI_dac104s085_DEVICE_NAME, SPI_BUS_NAME, (void*)&spi_cs);
if (res != RT_EOK)
{
OLED_TRACE("rt_spi_bus_attach_device!\r\n");
return res;
}
/* config spi */
{
struct rt_spi_configuration cfg;
cfg.data_width = 16;
cfg.mode = RT_SPI_MODE_1 | RT_SPI_MSB;
cfg.max_hz = 40 * 1000 *1000; /* 40M,SPI*/
rt_spi_configure(&spi_dev_dac104s085, &cfg);
}
return RT_EOK;
}
static int rt_hw_dac104s0835_init(void)
{
rt_hw_dac104s085_config();
return 0;
}
INIT_PREV_EXPORT(rt_hw_dac104s0835_init);
定时器的例子可以下载我上传的资源。
https://download.csdn.net/download/lilijianqun/11570745
四驱动流程
1外设的初始化在完整的rtthread的工程中直接使用INIT_BOARD_EXPORT()命令导出去进行自动初始化操作,这个命令调用的是rtthread中的自动初始化功能,
2启动流程
$Sub$$main()这个是系统启动最先调用的函数,之后调用rtthread_startup()函数去调用各种必要初始化条件,这些基本重要的函数都在components.c文件中可以查到
五开发:
线程,信号量,信号队列,等看文档开发rtrthread编程指南
六移植:
最主要的在于板卡的支持,其中主要是时钟树的驱动(系统时钟,滴答定时器)和中断的处理
{contextdrv.s中
中断主要是实现上下文切换:
{
“这几个的实现可以参考rtrthread编程指南”
rt_hw_context_switch_to()
rt_hw_context_switch()
rt_hw_context_switch_interrupt()
}
PendSV_Handler
rt_hw_interrupt_disable 关中断
rt_hw_interrupt_enable 开中断
rt_hw_interrupt_thread_switch
}