- 开发平台:RT-Thread Studio
- 开发板:潘多拉 STM32L475VET6、战舰板 STM32F103ZET6
- RTThread版本:4.0.3
- 芯片包版本:0.1.9(F1、F4、L4的都是)
一. 创建工程及时钟
1.1 创建工程
文件 -> 新建 -> RT-Thread 项目。

创建工程后,直接编译,无报错和警告
如果这个时候直接编译不通过的,在包管理器中降低芯片包的版本,不要用最新的,最新的更多会适配高版本的rtthread,比如为了适配4.1.1而新发的芯片包。(L4芯片的就有这个问题)

下载至开发板
打开串口助手可以看到内容输出。

1.2 时钟分析
根据提示,默认使用的是内部时钟,如果需要修改,请修改drv_clk.c文件。
分析一下,根据启动文件,可以梳理一下启动过程。

从这里启动

然后在 rt-thread -> src -> components.c 文件中能看到包含int entry(void)在内的所有初始化函数。
从int entry(void);函数中进入rtthread_startup();函数,再从中进入rt_hw_board_init();函数,再从中进入hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);函数。
这里面传入的参数是定义在 board.h 文件中的,默认如下:
/————————– CLOCK CONFIG BEGIN ————————–/
#define BSP_CLOCK_SOURCE (“HSI”)
#define BSP_CLOCK_SOURCE_FREQ_MHZ ((int32_t)0)
#define BSP_CLOCK_SYSTEM_FREQ_MHZ ((int32_t)80)
/————————– CLOCK CONFIG END ————————–/
但是,继续往下翻下去,发现传入的这几个参数根本没用。
再从中进去clk_init(clock_src, clock_src_freq, clock_target_freq);函数。这个函数就定义在drv_clk.c文件中。

该函数又调用了system_clock_config(target_freq);函数,该函数内容如下,这里的时钟配置为默认使用的内部高速时钟。

可以看出,最后这里是时钟初始化的地方。需要修改时钟只需要修改此函数即可。
可以看出,board.h中的那几个宏定义在传参的过程中,最后也没传过来。(即 没用)
1.3 时钟配置
最简单的配置时钟的方法还是,使用cubemx生成一个。
从左侧打开cubemx窗口,然后配置上调试接口、时钟树、串口,然后选择makefile,直接生成工程,其他的都没有改动。生成完毕之后,然后关闭cubemx界面(后续都是关闭界面之后才生效,就不再写了),在左边可以看到cubemx文件夹。
注意:
- 串口的配置,打开就行,具体的参数可以不用管其实。
- 如果没看到这个文件夹,就鼠标在工程栏选中工程,然后右键刷新一下。

生成完成之后还会有提示有个文件被重命名了。
如果没弹出来,注意cubemx界面是否关掉了。一定要关掉才可以。
点击cubemx的时候,有时候会不小心点到多次,打开了两个cubemx界面,都要关掉才行。

因为cubemx根据你的配置重新生成了一份配置文件,所以原有的那一份就给你换了个名字做备份。而且后续每次使用cubemx生成代码后,该文件中的宏定义会自动打开,就不用手动打开了。
为什么在上面的配置中需要勾选串口,是因为,默认的配置文件中是打开了串口的定义的,但是使用cubemx重新生成的话,不勾选串口,生成的配置文件中不会自动添加使用串口的宏定义。

另外,再次打开上面提到的drv_clk.c文件查看clk_init函数。
可以发现使用cubemx生成之后,这个函数被自动更改了,提示我们这个函数现在使用的是cubemx生成的内容了。而且此时跟随SystemClock_Config();函数定义的位置,可以发现,它会跳到cubemx生成的那个main.c文件中的时钟初始化部分,即调用的是新生成的时钟初始化函数。

而且,在cubemx生成的main.c文件中的main函数中,在定义的前面自动加了**__WEAK**标识。
然后,点击编译,应该也是可以通过的,无错误无警告。
如果有串口相关错误:检查cubemx是否打开了串口一的配置(SHELL默认用的串口一,但是实际上随便勾选一个串口,让cubemx把串口的头文件打开就行了,具体的引脚初始化函数其实没用到)
二. 设备框架使用
以下并不一定是按照从简单到麻烦的顺序来的。
2.1 PIN设备
pin设备默认是开启的,直接使用即可。
2.1.1 LED闪烁
在工程中新建一个文件夹,在文件夹中新建一个.c文件,添加以下内容。
编译下载后,在Shell窗口输入led_thread_test命令,可以看到两个LED闪烁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
#include <rtdevice.h> #include "drv_common.h" #include <rtthread.h>
typedef struct { rt_uint32_t flash_ms; rt_uint8_t led_index; }Parameter_struct;
#define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 5
#define LED0_PIN GET_PIN(B, 5) #define LED1_PIN GET_PIN(E, 5)
#define LED0 0 #define LED1 1
Parameter_struct Parameter0_struct = { .flash_ms = 1000, .led_index = LED0, };
Parameter_struct Parameter1_struct = { .flash_ms = 2000, .led_index = LED1, };
static rt_thread_t led_thread = NULL;
static void led_thread_entry (void *parameter) { Parameter_struct *in_parameter = (Parameter_struct *)parameter;
rt_uint32_t count = 0; while(1) { switch(in_parameter->led_index) { case LED0: rt_kprintf("led0 flash \n"); rt_pin_write(LED0_PIN, count % 2); break;
case LED1: rt_kprintf("led1 flash \n"); rt_pin_write(LED1_PIN, count % 2); break;
default: rt_kprintf("please enter the correct LED index.(0/1) \n"); } count++; rt_thread_mdelay(in_parameter->flash_ms); } }
static int led_thread_test(void) { rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); rt_pin_write(LED0_PIN, 1); rt_pin_write(LED1_PIN, 1);
if(Parameter0_struct.flash_ms != 0) { led_thread = rt_thread_create("led0_th", led_thread_entry, (void*)&Parameter0_struct, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (led_thread != RT_NULL) rt_thread_startup(led_thread); else { rt_kprintf("led0 flash thread is failed...the error code is %ld \r\n", led_thread->error); } }
led_thread = RT_NULL;
if(Parameter1_struct.flash_ms != 0) { led_thread = rt_thread_create("led1_th", led_thread_entry, (void*)&Parameter1_struct, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (led_thread != RT_NULL) rt_thread_startup(led_thread); else { rt_kprintf("led1 flash thread is failed...the error code is %ld \r\n", led_thread->error); } }
return RT_EOK; }
MSH_CMD_EXPORT(led_thread_test, led thread test);
|
2.1.2 按键中断
在工程中新建一个文件夹,在文件夹中新建一个.c文件,添加以下内容。
编译下载后,在Shell窗口输入key_irq_test命令,然后按下按键,对应LED亮起。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
#include <rtthread.h> #include <rtdevice.h>
#include "drv_common.h"
#define KEY0_PIN GET_PIN(E, 4) #define KEY2_PIN GET_PIN(E, 2)
#define LED0_PIN GET_PIN(B, 5) #define LED1_PIN GET_PIN(E, 5)
void led0_on(void *args) { rt_kprintf("led0 on, led1 off!\n"); rt_pin_write(LED0_PIN, PIN_LOW); rt_pin_write(LED1_PIN, PIN_HIGH); }
void led1_on(void *args) { rt_kprintf("led0 off, led1 on!\n"); rt_pin_write(LED0_PIN, PIN_HIGH); rt_pin_write(LED1_PIN, PIN_LOW); }
static void key_irq_test(void) { rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
rt_pin_write(LED0_PIN, PIN_HIGH); rt_pin_write(LED1_PIN, PIN_HIGH);
rt_pin_mode(KEY0_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(KEY0_PIN, PIN_IRQ_MODE_FALLING, led0_on, RT_NULL); rt_pin_irq_enable(KEY0_PIN, PIN_IRQ_ENABLE);
rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_attach_irq(KEY2_PIN, PIN_IRQ_MODE_FALLING, led1_on, RT_NULL); rt_pin_irq_enable(KEY2_PIN, PIN_IRQ_ENABLE); }
MSH_CMD_EXPORT(key_irq_test, key irq test);
|
2.2 串口设备
目标:串口二接收串口助手发来的数据,并且将数据重新发送出去。
此设备的开启步骤在board.h中是有描述的。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用UART设备驱动程序 -> 使能串口DMA模式,勾选上。
使用UART设备驱动程序 默认是打开的,因为串口一用于SHELL窗口了。这里只要把下面的DMA点开即可。
第二步:在 board.h 中,添加如下宏定义。
1 2 3 4 5 6
| #define BSP_USING_UART2 #define BSP_UART2_TX_PIN "PA2" #define BSP_UART2_RX_PIN "PA3"
#define BSP_UART2_RX_USING_DMA
|
此时如果直接编译,然后在Shell窗口输入list_device命令,就已经能看到uart2设备了。
第三步:编写测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
|
#include <rtthread.h>
struct rx_msg { rt_device_t dev; rt_size_t size; };
#define TEST_UART_NAME "uart2"
static rt_device_t usart_serial;
static struct rt_messagequeue usart_rx_mq;
static rt_err_t uart_input(rt_device_t dev, rt_size_t size) { struct rx_msg msg; rt_err_t result;
msg.dev = dev; msg.size = size;
result = rt_mq_send(&usart_rx_mq, &msg, sizeof(msg));
if ( result == -RT_EFULL) { rt_kprintf("message queue full!\n"); } return result; }
static void serial_thread_entry(void *parameter) { struct rx_msg msg; rt_err_t result; rt_uint32_t rx_length;
static char rx_buffer[RT_SERIAL_RB_BUFSZ + 1];
while (1) { rt_memset(&msg, 0, sizeof(msg));
result = rt_mq_recv(&usart_rx_mq, &msg, sizeof(msg), RT_WAITING_FOREVER);
if (result == RT_EOK) { rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
rx_buffer[rx_length] = '\0';
rt_device_write(usart_serial, 0, rx_buffer, rx_length);
rt_kprintf("%s\n", rx_buffer); } } }
static int uart2_dma_test(void) { rt_err_t ret = RT_EOK;
static char msg_pool[256];
char str[] = "usart2 test is running!\r\n";
usart_serial = rt_device_find(TEST_UART_NAME); if (!usart_serial) { rt_kprintf(TEST_UART_NAME); rt_kprintf("not find!\n");
return RT_ERROR; }
rt_device_open(usart_serial, RT_DEVICE_FLAG_DMA_RX);
rt_mq_init(&usart_rx_mq, "usart_rx_mq", msg_pool, sizeof(struct rx_msg), sizeof(msg_pool), RT_IPC_FLAG_FIFO); rt_device_set_rx_indicate(usart_serial, uart_input);
rt_device_write(usart_serial, 0, str, (sizeof(str) - 1)); rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10); if (thread != RT_NULL) { rt_thread_startup(thread); } else { ret = RT_ERROR; }
return ret; }
MSH_CMD_EXPORT(uart2_dma_test, uart2 device dma test);
|
编译运行后,在SHELL窗口(串口一)输入uart2_dma_test命令,然后在串口二中发送信息,串口二会将收到的信息重新发送。
如果想要使用串口一,需要做出的改动如下:
- board.c中的宏定义
1 2 3 4
| #define BSP_USING_UART1 #define BSP_UART1_TX_PIN "PA9" #define BSP_UART1_RX_PIN "PA10" #define BSP_UART1_RX_USING_DMA
|
在 RT-Thread Settings 中 -> 组件 -> shell命令中,将其关闭。
将上述程序中的#define TEST_UART_NAME "uart2",2 改为 1。
其余的函数名、命令名改不改都行,不影响运行,要能分辨就行。
关于串口发送DMA
经测试,在部分芯片包中貌似不支持发送DMA。(理由未知)
在潘多拉STM32L475VET6中,增加#define BSP_UART2_TX_USING_DMA宏定义会报错。
而在电子料架板卡STM32F407VET6中,增加#define BSP_UART2_TX_USING_DMA宏定义就可以正常编译通过。
2.3 CAN(需要改驱动)
目标:使用CAN接口发送和接收数据。
此设备的开启步骤在board.h中没有相关描述。但是有BUG。
硬件配置
- 硬件设备:CANelyst-Ⅱ,设备的CAN_L与开发板的L连接,设备的CAN_H与开发板的H连接。
- 软件:USB_CAN TOOL,切记切记要安装驱动,否则会一直显示未找到设备。
可以先按照软件上方目录 -> 设备操作 -> USBCAN测试工具中的操作指引,将硬件两个端口连起来,测试硬件是否正常运行,然后在搞软件。
常见问题:
- 设备一直未找到(无法连接)。打开电脑设备管理器看CAN设备是否正常识别,如果未识别,说明没安装驱动。安装驱动方法参考:软件上方目录 -> 帮助 -> 帮助文档 -> 2. USB驱动安装与卸载说明书.pdf
程序
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> CAN设备驱动程序,勾选上 -> 使能CAN硬件过滤器,也先勾选上。
硬件过滤器:作用未知,等了解了过来写上。
第二步:添加驱动程序。在RT-Thread Studio 软件的安装目录下:D:\RT-ThreadStudio\repo\Extract\RT-Thread_Source_Code\RT-Thread\4.0.3\bsp\stm32\libraries\HAL_Drivers\
找到drv_can.c放到我们工程的drivers文件夹,找到drv_can.h文件放到我们工程的drivers\include文件夹。
第三步:添加以下宏定义到board.h文件中,这里使用 CAN1 进行测试。注意:下面这部分宏定义都是自己添加的,默认是没有关于CAN接口的宏定义的。
1 2 3 4 5 6 7
|
#define BSP_USING_CAN #define BSP_USING_CAN1
|
第四步:从左侧窗口打开cubemx,勾选上CAN接口,引脚是对的就可以,参数配置可以不用管。然后关闭cubemx界面,生成代码后刷新左侧目录界面。即可在cubemx文件夹中的stm32f1xx_hal_msp.c文件中看到CAN的引脚初始化代码。
此时如果直接编译,然后在Shell窗口输入list_device命令,就已经能看到can1设备了。
第五步:编写测试代码,程序运行后,打开CAN-USB软件并连接设备,波特率设置1000k,然后在Shell窗口输入can_test命令,即可从CAN-USB软件中看到开发板发送的消息。也可以使用CAN-USB软件发送消息,在Shell窗口可看到接收的消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
#include <rtthread.h> #include "rtdevice.h"
#define CAN_DEV_NAME "can1"
static struct rt_semaphore rx_sem; static rt_device_t can_dev;
static rt_err_t can_rx_call(rt_device_t dev, rt_size_t size) { rt_sem_release(&rx_sem);
return RT_EOK; }
static void can_rx_thread(void *parameter) { int i; rt_err_t res; struct rt_can_msg rxmsg = {0};
rt_device_set_rx_indicate(can_dev, can_rx_call);
#ifdef RT_CAN_USING_HDR struct rt_can_filter_item items[5] = { RT_CAN_FILTER_ITEM_INIT(0x100, 0, 0, 0, 0x700, RT_NULL, RT_NULL), RT_CAN_FILTER_ITEM_INIT(0x300, 0, 0, 0, 0x700, RT_NULL, RT_NULL), RT_CAN_FILTER_ITEM_INIT(0x211, 0, 0, 0, 0x7ff, RT_NULL, RT_NULL), RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), {0x555, 0, 0, 0, 0x7ff, 7,} }; struct rt_can_filter_config cfg = {5, 1, items}; res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); RT_ASSERT(res == RT_EOK); #endif
while (1) { rxmsg.hdr = -1; rt_sem_take(&rx_sem, RT_WAITING_FOREVER); rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg)); rt_kprintf("ID:%x", rxmsg.id); for (i = 0; i < 8; i++) { rt_kprintf("%2x", rxmsg.data[i]); }
rt_kprintf("\n"); } }
int can_test(int argc, char *argv[]) { struct rt_can_msg msg = {0}; rt_err_t res; rt_size_t size; rt_thread_t thread; char can_name[RT_NAME_MAX];
if (argc == 2) { rt_strncpy(can_name, argv[1], RT_NAME_MAX); } else { rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX); }
can_dev = rt_device_find(can_name); if (!can_dev) { rt_kprintf("find %s failed!\n", can_name); return RT_ERROR; }
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
res = rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX); RT_ASSERT(res == RT_EOK);
thread = rt_thread_create("can_rx", can_rx_thread, RT_NULL, 1024, 25, 10); if (thread != RT_NULL) { rt_thread_startup(thread); } else { rt_kprintf("create can_rx thread failed!\n"); }
msg.id = 0x78; msg.ide = RT_CAN_STDID; msg.rtr = RT_CAN_DTR; msg.len = 8;
msg.data[0] = 0x00; msg.data[1] = 0x11; msg.data[2] = 0x22; msg.data[3] = 0x33; msg.data[4] = 0x44; msg.data[5] = 0x55; msg.data[6] = 0x66; msg.data[7] = 0x77; size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); if (size == 0) { rt_kprintf("can dev write data failed!\n"); }
for(rt_uint8_t send_ind = 0; send_ind < 10; send_ind++) { rt_thread_mdelay(1000);
msg.data[0] = msg.data[0] + 0x01; msg.data[1] = msg.data[1] + 0x01; msg.data[2] = msg.data[2] + 0x01; msg.data[3] = msg.data[3] + 0x01; msg.data[4] = msg.data[4] + 0x01; msg.data[5] = msg.data[5] + 0x01; msg.data[6] = msg.data[6] + 0x01; msg.data[7] = msg.data[7] + 0x01; size = rt_device_write(can_dev, 0, &msg, sizeof(msg)); if (size == 0) { rt_kprintf("can dev write data failed!\n"); } }
return res; }
MSH_CMD_EXPORT(can_test, can device sample);
|
另外:上述程序使用F1的开发板没有出现问题,但是使用其他板卡(F407VET6)上一直发送失败。
问题和解决办法如下:https://club.rt-thread.org/ask/article/5cedb728813e6fd8.html
但是相比于上面文章,多更改了关于波特率表的设置,自己测试一下波特率250和500k的需不需要改。
修改后的CAN驱动文件在文件夹中附上了。
2.4 ADC
目标:串口打印ADC的值。
此设备的开启步骤在board.h中是有描述的。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用ADC设备驱动程序,勾选上。
第二步:在board.h中打开#define BSP_USING_ADC1宏定义。
这里测试使用的引脚为PA1(ADC1的通道1)。
第三步:从左侧窗口打开cubemx,勾选上ADC1的CH1,确认一下引脚是不是对的,参数配置可以不用管。ADC打开了,时钟树配置界面ADC的时钟也要配置一下,不能超过14Mhz(ADC的需求)。然后关闭cubemx界面,生成代码后刷新左侧目录界面。即可在cubemx文件夹中的stm32f1xx_hal_msp.c文件中看到ADC的引脚初始化代码。
此时如果直接编译,然后在Shell窗口输入list_device命令,就已经能看到adc1设备了。
第四步:编写测试代码,程序运行后,在Shell窗口输入adc_vol_test命令,即可以在Shell窗口看到连续发送50次的ADC采样值和电压值。在此过程中改变PA1接触的地方,可以看到电压值的改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
#include <rtthread.h> #include <rtdevice.h>
#define ADC_DEV_NAME "adc1" #define ADC_DEV_CHANNEL 1 #define REFER_VOLTAGE 330 #define CONVERT_BITS (1 << 12)
static int adc_vol_test(int argc, char *argv[]) { rt_adc_device_t adc_dev; rt_uint32_t value, vol; rt_err_t ret = RT_EOK;
adc_dev = (rt_adc_device_t)rt_device_find(ADC_DEV_NAME);
if (adc_dev == RT_NULL) { rt_kprintf("adc test run failed! can't find %s device!\n", ADC_DEV_NAME); return RT_ERROR; }
ret = rt_adc_enable(adc_dev, ADC_DEV_CHANNEL);
for(rt_uint16_t times = 0; times <= 50; times++) { value = rt_adc_read(adc_dev, ADC_DEV_CHANNEL); vol = value * REFER_VOLTAGE / CONVERT_BITS; rt_kprintf("the value is :%d, the voltage is :%d.%02d \n", value, vol / 100, vol % 100); rt_thread_mdelay(500); }
ret = rt_adc_disable(adc_dev, ADC_DEV_CHANNEL);
return ret; }
MSH_CMD_EXPORT(adc_vol_test, adc voltage convert test);
|
2.5 IIC
目标:实现对24C02的读写功能。
此设备的开启步骤在board.h中是有描述的。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用GPIO模拟I2C,勾选上。
第二步:在board.h中打开#define BSP_USING_I2C1宏定义,并修改对应的引脚。注意这里的引脚是用的软件模拟,跟硬件IIC没关系,所以引脚用哪个写哪个。这里如下:
1 2 3 4 5
| #define BSP_USING_I2C1 #ifdef BSP_USING_I2C1 #define BSP_I2C1_SCL_PIN GET_PIN(B, 6) #define BSP_I2C1_SDA_PIN GET_PIN(B, 7) #endif
|
此时如果直接编译,然后在Shell窗口输入list_device命令,就已经能看到i2c1设备了。
第三步:编写24C02驱动代码及测试代码。
C文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| #include "drv_iic_24c02.h"
#define LOG_TAG "AT24C02" #define LOG_LVL LOG_LVL_DBG #include <rtdbg.h>
static struct rt_i2c_bus_device *i2c_bus = RT_NULL;
uint8_t at24c02_read_byte(uint8_t addr) { struct rt_i2c_msg msg; uint8_t buffer[2]; uint8_t data;
msg.addr = AT24C02_I2C_ADDR; msg.flags = RT_I2C_WR; buffer[0] = addr; msg.buf = buffer; msg.len = 1; rt_i2c_transfer(i2c_bus, &msg, 1);
msg.flags = RT_I2C_RD; msg.buf = &data; rt_i2c_transfer(i2c_bus, &msg, 1);
return data; }
rt_err_t at24c02_write_byte(uint8_t addr, uint8_t data) { struct rt_i2c_msg msg;
uint8_t buffer[2];
buffer[0] = addr; buffer[1] = data;
msg.addr = AT24C02_I2C_ADDR; msg.flags = RT_I2C_WR; msg.buf = buffer; msg.len = 2; if(rt_i2c_transfer(i2c_bus, &msg, 1) != 1) return RT_ERROR;
return RT_EOK; }
static rt_err_t at24c02_check(uint8_t check) { uint8_t temp;
temp = at24c02_read_byte(AT24C02_SIZE - 1); if(temp != check) { at24c02_write_byte(AT24C02_SIZE - 1, check); rt_thread_mdelay(5); temp = at24c02_read_byte(AT24C02_SIZE - 1); if(temp != check) return RT_ERROR; } return RT_EOK; }
rt_err_t at24c02_init() { i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(AT24C02_I2C_BUS_NAME); if (i2c_bus == RT_NULL) { LOG_D("can't find device! \n"); return RT_ERROR; }
if(at24c02_check(AT24C02_CHECK_VALUE) != RT_EOK) { LOG_D("at24c02 check fail! \n"); return RT_ERROR; } LOG_D("at24c02 check ok! \n"); return RT_EOK; }
|
头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #ifndef APPLICATIONS_ICODE_IIC_24C02_SUPPORT_DRV_IIC_24C02_H_ #define APPLICATIONS_ICODE_IIC_24C02_SUPPORT_DRV_IIC_24C02_H_
#include <rtthread.h> #include "drv_soft_i2c.h"
#define AT24C02_I2C_BUS_NAME "i2c1" #define AT24C02_I2C_ADDR 0x50 #define AT24C02_SIZE 256 #define AT24C02_CHECK_VALUE 0x5a
rt_err_t at24c02_init(); uint8_t at24c02_read_byte(uint8_t addr); rt_err_t at24c02_write_byte(uint8_t addr, uint8_t data);
#endif
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include "drv_iic_24c02.h" #include <rtthread.h>
static int iic_at24c02_test(void) { rt_uint8_t *read_write_read_buf = RT_NULL;
read_write_read_buf = (rt_uint8_t *)rt_malloc(AT24C02_SIZE);
at24c02_init();
for(rt_uint16_t addr = 0; addr < AT24C02_SIZE; addr++) { if(RT_EOK == at24c02_write_byte(addr, 0)) rt_kprintf("address %x erase OK.\n", addr); else rt_kprintf("address %x erase Failed.\n", addr); }
for(rt_uint16_t addr = 0; addr < AT24C02_SIZE; addr++) { read_write_read_buf[addr] = at24c02_read_byte(addr); rt_kprintf("the data at address %x is %d.\n", addr, read_write_read_buf[addr]); }
for(rt_uint16_t addr = 0; addr < AT24C02_SIZE; addr++) { if(RT_EOK == at24c02_write_byte(addr, addr)) rt_kprintf("address %x write OK.\n", addr); else rt_kprintf("address %x write Failed.\n", addr); }
for(rt_uint16_t addr = 0; addr < AT24C02_SIZE; addr++) { read_write_read_buf[addr] = at24c02_read_byte(addr); rt_kprintf("the data at address %x is %d.\n", addr, read_write_read_buf[addr]); }
rt_free(read_write_read_buf);
return RT_EOK; } MSH_CMD_EXPORT(iic_at24c02_test, iic at24c02 read write test);
|
程序运行后,在Shell窗口输入iic_at24c02_test命令,即可看到程序首先将芯片数据全部擦除,然后读取,读取之后写入,并再次读取查看是否是写入的数据。
2.6 SPI + DFS
目标:直接上文件系统。
这里用的潘多拉的板子,存储使用SD卡,接口是SPI。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用 SPI 总线/设备驱动程序,勾选上。
第二步:在board.h中打开#define BSP_USING_SPI1宏定义,根据自己实际使用的SPI接口设置即可。
第三步:从左侧窗口打开cubemx,SPI1,确认一下引脚是不是对的,参数配置可以不用管,然后关闭cubemx界面,生成代码后刷新左侧目录界面。即可在cubemx文件夹中的stm32l4xx_hal_msp.c文件中看到SPI的引脚初始化代码。
到了这里SPI就可以使用了。
第四步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用 SPI 总线/设备驱动程序 -> 使用 SPI SD/TF 卡驱动程序,勾选上。
勾选上保存之后,就能在 rt-thread -> components 中看到 dfs 文件夹。这个文件夹中的文件定义了系统挂载、打开文件、创建文件夹等等接口。
2.7 PWM(需要改驱动)
目标:PWM实现呼吸灯。
此设备的开启步骤在board.h中是有描述的。但是不全,而且有BUG。
第一步:在 RT-Thread Settings 中 -> 组件 -> 设备驱动程序 -> 使用 PWM 设备驱动程序,勾选上。
第二步:在board.h中打开#define BSP_USING_PWM3宏定义,并添加使用的通道宏定义#define BSP_USING_PWM3_CH2。
这里测试使用的引脚为PB5(TIM3的通道2)。根据自己使用的定时器和通道进行修改上述宏定义。
另外,通道宏定义board.h里面没写,要自己添加上。
第三步:从左侧窗口打开cubemx,勾选上TIM3的CH2,确认一下引脚是不是对的,参数配置可以不用管,然后关闭cubemx界面,生成代码后刷新左侧目录界面。即可在cubemx文件夹中的stm32f1xx_hal_msp.c文件中看到TIM的引脚初始化代码,一共有两个函数。
1 2
| void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base) void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
|
第四步:修改drv_pwm.c
在drv_pwm.c文件中的static rt_err_t stm32_hw_pwm_init(struct stm32_pwm *device)函数中,添加定时器的初始化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| static rt_err_t stm32_hw_pwm_init(struct stm32_pwm *device) { rt_err_t result = RT_EOK; TIM_HandleTypeDef *tim = RT_NULL; TIM_OC_InitTypeDef oc_config = {0}; TIM_MasterConfigTypeDef master_config = {0}; TIM_ClockConfigTypeDef clock_config = {0};
RT_ASSERT(device != RT_NULL);
tim = (TIM_HandleTypeDef *)&device->tim_handle;
tim->Init.Prescaler = 0; tim->Init.CounterMode = TIM_COUNTERMODE_UP; tim->Init.Period = 0; tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; #if defined(SOC_SERIES_STM32F1) || defined(SOC_SERIES_STM32L4) tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; #endif
if (HAL_TIM_Base_Init(tim) != HAL_OK) { LOG_E("%s tim init failed", device->name); result = -RT_ERROR; goto __exit; } if (HAL_TIM_PWM_Init(tim) != HAL_OK) { LOG_E("%s pwm init failed", device->name); result = -RT_ERROR; goto __exit; } ... }
|
第五步:添加测试例程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
#include <rtthread.h> #include <rtdevice.h>
#define PWM_DEV_NAME "pwm3" #define PWM_DEV_CHANNEL 2
struct rt_device_pwm *pwm_dev;
static rt_thread_t pwm_thread = NULL;
#define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 512 #define THREAD_TIMESLICE 5
static void pwm_led_thread_entry(void *parameter) { rt_uint32_t period, pulse, dir;
period = 500000; dir = 1; pulse = 0;
pwm_dev = (struct rt_device_pwm *)rt_device_find(PWM_DEV_NAME); if (pwm_dev == RT_NULL) { rt_kprintf("pwm sample run failed! can't find %s device!\n", PWM_DEV_NAME); }
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); rt_pwm_enable(pwm_dev, PWM_DEV_CHANNEL);
while (1) { rt_thread_mdelay(50); if (dir) { pulse += 5000; } else { pulse -= 5000; } if (pulse >= period) { dir = 0; } if (0 == pulse) { dir = 1; }
rt_pwm_set(pwm_dev, PWM_DEV_CHANNEL, period, pulse); } }
static int pwm_thread_test(void) { pwm_thread = rt_thread_create("pwm_th", pwm_led_thread_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (pwm_thread != RT_NULL) rt_thread_startup(pwm_thread); else { rt_kprintf("pwm led thread is failed...the error code is %ld \r\n", pwm_thread->error); }
return RT_EOK; }
MSH_CMD_EXPORT(pwm_thread_test, pwm thread test);
|
2.8 网口