- 2024-11-03
-
回复了主题帖:
【极海APM32F407 Tiny Board】 5.基于LWIP的IAP升级
博主,能否提供一些完整的参考代码
- 2024-09-16
-
回复了主题帖:
NUCLEO-H533RE开发板测评10(LVGL综合应用)
老哥,有没有完整的工程代码
- 2024-05-31
-
回复了主题帖:
MicroPython 移植到 WCH-CH32V307 RISC-V 单片机
MicroPython 移植到 WCH-CH32V307 RISC-V 单片机
- 2024-03-24
-
回复了主题帖:
测评颁奖: 雅特力车规级MCU开发板AT-START-A403A
吾妻思萌 发表于 2024-3-23 15:37
第一名 的mi zu ji 老哥这是啥意思啊
没有啥子意思,现在想改名字,发现论坛不好改
- 2024-03-15
-
回复了主题帖:
车规级的MCU芯片,与常规芯片开发有何不同?
近几年汽车电子的快速发展,国产的车规级微控制器也出来不少,在测评雅特力AT32A403A开发板过程中,刚开始还不知道有啥子区别,但是经过一段时间的使用下来,在测评雅特力AT32A403A开发板时,我使用AT32F403A系列开发板的程序也能够完美运行,感觉车规级MCU功能与普通MCU区别不大,甚至感觉基本上大差不差,除了车规级MCU对于安全性要求很高,工作的温度的范围也要比常规的高一些,毕竟能通过车规级认证还是比较难的,而且车规级MCU认证也有不少级别,支持国产吧,虽然路还很长
- 2024-03-09
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 14 ADC外设测试案例 RT-Thread ADC设备驱动测试
本帖最后由 みずじ 于 2024-3-9 11:18 编辑
## 雅特力车规级MCU-AT32A403A开发板评测 14 ADC外设测试案例 RT-Thread ADC设备驱动测试
### 1. 软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. RT-Thread V4.1版本源码
4. ADC
### 2. ADC
ADC(Analog-to-Digital Converter) 指模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。模数转换器可以实现这个功能,在各种不同的产品中都可以找到它的身影。
AT32A403A系列产品,内嵌3个12位的模拟/数字转换器(ADC),共享多达16个外部通道,可以实 现单次或序列转换。在序列模式下,自动进行在选定的一组模拟输入上的转换。
ADC接口上的其它逻辑功能包括:
- 同时的采样和保持
- 位移的采样和保持
- 单次采样
输入管脚介绍:
VDDA :模拟电源,ADC 模拟电源
VSSA :模拟电源地,ADC 模拟电源地
VREF+ :模拟参考正极,ADC 使用的高端/正极模拟参考电压
VREF- :模拟参考负极,ADC 使用的低端/负极参考电压
ADCx_IN :模拟输入信号通道
#### 转换过程
如下图所示模数转换一般要经过采样、保持和量化、编码这几个步骤。在实际电路中,有些过程是合并进行的,如采样和保持,量化和编码在转换过程中是同时实现的。
采样是将时间上连续变化的模拟信号转换为时间上离散的模拟信号。采样取得的模拟信号转换为数字信号都需要一定时间,为了给后续的量化编码过程提供一个稳定的值,在采样电路后要求将所采样的模拟信号保持一段时间。
将数值连续的模拟量转换为数字量的过程称为量化。数字信号在数值上是离散的。采样保持电路的输出电压还需要按照某种近似方式归化到与之相应的离散电平上,任何数字量只能是某个最小数量单位的整数倍。量化后的数值最后还需要编码过程,也就是 A/D 转换器输出的数字量。
#### 分辨率
分辨率以二进制(或十进制)数的位数来表示,一般有8位、10位、12位、16位等,它说明模数转换器对输入信号的分辨能力,位数越多,表示分辨率越高,恢复模拟信号时会更精确。
#### 精度
精度表示 ADC 器件在所有的数值点上对应的模拟值和真实值之间的最大误差值,也就是输出数值偏离线性最大的距离。
注:精度与分辨率是两个不一样的概念,请注意区分。
#### 转换速率
转换速率是指 A/D 转换器完成一次从模拟到数字的 AD 转换所需时间的倒数。例如,某 A/D 转换器的转换速率为 1MHz,则表示完成一次 AD 转换时间为 1 微秒。
#### 3. RT-Thread 访问 ADC 设备 API接口
应用程序通过 RT-Thread 提供的 ADC 设备管理接口来访问 ADC 硬件,相关接口如下所示:
**find read wirte close open这些操作都是类似linux中的操作,一切都是文件**
| **函数** | **描述** |
| ---------------- | ------------------------------------- |
| rt_device_find() | 根据 ADC 设备名称查找设备获取设备句柄 |
| rt_adc_enable() | 使能 ADC 设备 |
| rt_adc_read() | 读取 ADC 设备数据 |
| rt_adc_disable() | 关闭 ADC 设备 |
### 4. RT-Thread ADC设备驱动测试
1. 在RT-Thread工程模板下,添加相关adc驱动
2. 添加相关头文件
3. 添加宏定义
4. 添加ADC测试案例代码
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/adc/adc?id=adc-%e7%ae%80%e4%bb%8b
```c
#include
#include
#include "board.h"
#include "drv_gpio.h"
#include "drv_adc.h"
#define ADC_DEV_NAME "adc1" /* ADC 设备名称 */
#define ADC_DEV_CHANNEL 5 /* ADC 通道 */
#define REFER_VOLTAGE 330 /* 参考电压 3.3V,数据精度乘以100保留2位小数*/
#define CONVERT_BITS (1
- 2024-03-06
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 13 RTC外设测试 OELD模块时钟显示 RT-thread...
本帖最后由 みずじ 于 2024-3-6 22:10 编辑
## 雅特力车规级MCU-AT32A403A开发板评测 13 RTC外设测试 OELD模块时钟显示 RT-thread RTC设备驱动测试
### 1. 软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. RT-Thread V4.1版本源码
4. RTC
### 2. RTC
RTC 计数逻辑位于电池供电域,内部为一个 32 位递增计数器,只要电池供电域有电,RTC 便会一 直运行,不受系统复位以及 VDD 掉电影响,RTC 主要具有以下功能:
1. 日历功能:32 位计数器,通过转换得到年、月、日、时、分、秒
2. 闹钟功能
3. 入侵检测功能
4. 校准功能
#### RTC日历功能
RTC 内部是一个 32 位的计数器,通常使用中该计数器 1 秒增加 1,也就是该计数器相当于秒钟,然 后根据当前的秒钟值,通过转换得到年、月、日、星期、时、分、秒,实现日历的功能,修改计数器 的值便可修改时间和日期。根据使用需要还可以产生秒中断:若秒中断使能(TSIEN=1),每隔一秒产生一个秒中断。
秒钟转换成日历 先规定一个起始时间,例如 1970-1-1 00:00:00 对应计数器为 0,现在比如计数值为 200000,那么 换算成时间为:
- 天数:200000 / 86400 = 2
- 小时:(200000 % 86400) / 3600= 7
- 分钟:(200000 % 3600) / 60= 33
- 秒钟:200000 % 60 = 20
##### 设置日历值(日历转换成秒钟)
```c
/**
* @brief set time. convert the input clock to a second.
* the time basic : 1970.1.1
* legitimate year: 1970 ~ 2099
* @param calendar
* @retval 0: set time right.
* 1: set time failed.
*/
uint8_t rtc_time_set(calendar_type *calendar)
{
uint32_t t;
uint32_t seccount = 0;
if(calendar->year < 1970 || calendar->year > 2099)
{
return 1;
}
for(t = 1970; t < calendar->year; t++)
{
if(is_leap_year(t))
{
seccount += 31622400;
}
else
{
seccount += 31536000;
}
}
calendar->month -= 1;
for(t = 0; t < calendar->month; t++)
{
seccount += (uint8_t)mon_table[t] * 86400;
if(is_leap_year(calendar->year) && t == 1)
{
seccount += 86400;
}
}
seccount += (uint8_t)(calendar->date - 1) * 86400;
seccount += (uint8_t)calendar->hour * 3600;
seccount += (uint8_t)calendar->min * 60;
seccount += calendar->sec;
/* enable pwc and bpr clocks */
crm_periph_clock_enable(CRM_PWC_PERIPH_CLOCK, TRUE);
crm_periph_clock_enable(CRM_BPR_PERIPH_CLOCK, TRUE);
/* enable write access to bpr domain */
pwc_battery_powered_domain_access(TRUE);
/* set the rtc counter value */
rtc_counter_set(seccount);
/* wait for the register write to complete */
rtc_wait_config_finish();
return 0;
}
```
### 3. 软件设计流程
1. 开启 PWC、BPR 时钟
2. 解锁电池供电域写保护
3. 检查日历是否已经初始化,如果正确就跳过初始化,如果不正确就初始化日历以及闹钟 (初始化日历时间 2024-03-06 19:40:00)
4. 主函数里每秒打印一次日历信息
5. 在 2024-03-06 19:40:15 时刻发生闹钟,串口进行打印并且LED亮。
```c
#include "main.h"
void oled_example(void)
{
//OLED 汉字显示 雅特力科技
OLED_ShowCHinese(0,0,13);
OLED_ShowCHinese(16,0,14);
OLED_ShowCHinese(32,0,15);
OLED_ShowCHinese(48,0,16);
OLED_ShowCHinese(64,0,17);
OLED_ShowString_08x16(96,0,"RTC");
}
char *weekday_table[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
/**
* @brief init alarm.
* @param none
* @retval none
*/
void alarm_init(void)
{
calendar_type alarm_struct;
/* clear alarm flag */
rtc_flag_clear(RTC_TA_FLAG);
/* wait for the register write to complete */
rtc_wait_config_finish();
/* configure and enable rtc interrupt */
nvic_irq_enable(RTC_IRQn, 0, 0);
/* enable alarm interrupt */
rtc_interrupt_enable(RTC_TA_INT, TRUE);
/* wait for the register write to complete */
rtc_wait_config_finish();
/* config alarm */
alarm_struct.year = 2024;
alarm_struct.month = 3;
alarm_struct.date = 6;
alarm_struct.hour = 19;
alarm_struct.min = 40;
alarm_struct.sec = 15;
rtc_alarm_clock_set(&alarm_struct);
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
// unsigned char count_num;
calendar_type time_struct;
/* config nvic priority group */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
system_clock_config();
at32_board_init();
uart_print_init(115200);
oled_init();
/* config init calendar */
time_struct.year = 2024;
time_struct.month = 3;
time_struct.date = 6;
time_struct.hour = 19;
time_struct.min = 40;
time_struct.sec = 0;
rtc_init(&time_struct);
/* config alarm */
alarm_init();
printf("initial ok\r\n");
printf("Hardware_Init [ok] \r\n");
printf("at_start_a403a board testing 2024-03-06\r\n");
printf("at_start_a403a board module softiic oled \r\n");
OLED_Clear();
printf("rtc_oled_example_test [ok] \r\n");
oled_example();
while(1)
{
if(rtc_flag_get(RTC_TS_FLAG) != RESET)
{
at32_led_toggle(LED3);
/* get time */
rtc_time_get();
/* print time */
printf("%d/%d/%d ", calendar.year, calendar.month, calendar.date);
printf("%02d:%02d:%02d %s\r\n", calendar.hour, calendar.min, calendar.sec, weekday_table[calendar.week]);
OLED_ShowNumber_UnsignedInteger_08x16(0,2,calendar.year,4);
OLED_ShowString_08x16(32,2,"-");
OLED_ShowNumber_UnsignedInteger_08x16(40,2,calendar.month,2);
OLED_ShowString_08x16(56,2,"-");
OLED_ShowNumber_UnsignedInteger_08x16(64,2,calendar.date,2);
OLED_ShowNumber_UnsignedInteger_08x16(0,4,calendar.hour,2);
OLED_ShowString_08x16(16,4,":");
OLED_ShowNumber_UnsignedInteger_08x16(24,4,calendar.min,2);
OLED_ShowString_08x16(40,4,":");
OLED_ShowNumber_UnsignedInteger_08x16(48,4,calendar.sec,2);
OLED_ShowString_08x16(32,6,weekday_table[calendar.week]);
/* wait for the register write to complete */
rtc_wait_config_finish();
/* clear the rtc second flag */
rtc_flag_clear(RTC_TS_FLAG);
/* wait for the register write to complete */
rtc_wait_config_finish();
}
}
}
```
### 4.测试效果
1. 闹钟定时效果
2. 复位重启,时间继续进行(恢复原始状态,需要断电)
3. OLED模块显示效果
[localvideo]3afc94b99171bf3b1c3d719625ac2f16[/localvideo]
### 5.RT-Thread RTC设备驱动测试
RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。本质上设备驱动框架层、设备驱动层都是都是对于原本的库函数进行二次封装,最后提供一个统一的API接口共应用层开发。在本文使用了RTC设备。
RTC (Real-Time Clock)实时时钟可以提供精确的实时时间,它可以用于产生年、月、日、时、分、秒等信息。目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。有些时钟芯片为了在主电源掉电时还可以工作,会外加电池供电,使时间信息一直保持有效。
RT-Thread 的 RTC设备为操作系统的时间系统提供了基础服务。面对越来越多的 IoT 场景,RTC 已经成为产品的标配,甚至在诸如 SSL 的安全传输过程中,RTC 已经成为不可或缺的部分。
#### 访问 RTC 设备
在开启 RTC 设备框架以及 RTC 驱动之后,用户可以 `#include ` 用来引用标准的时间操作函数(例如 time、ctime、stime、mktime等,具体使用方法可以百度)。在Unix系统或者Windows系统下怎么使用 `` 里边的函数,在RT-Thread下就怎么使用。**建议用户采用[标准库时间函数](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/libc/iso-ansi-c?id=5-时间函数)来操作 RTC。**
1. 在RT-Thread工程模板下,添加相关RTC驱动
2. 添加相关头文件,重点是time.h的添加,如果不添加,会调用MDK提供的底层库,但是会报错
3. 添加RTC测试案例代码
```c
#include
#include
#include "board.h"
#include "drv_gpio.h"
#define RTC_NAME "rtc"
int main()
{
rt_err_t ret = RT_EOK;
time_t now;
rt_device_t device = RT_NULL;
/* 寻找设备 */
device = rt_device_find(RTC_NAME);
if (!device)
{
rt_kprintf("find %s failed!", RTC_NAME);
return RT_ERROR;
}
/* 初始化RTC设备 */
if(rt_device_open(device, 0) != RT_EOK)
{
rt_kprintf("open %s failed!", RTC_NAME);
return RT_ERROR;
}
/* 设置日期 */
ret = set_date(2024, 3, 6);
if (ret != RT_EOK)
{
rt_kprintf("set RTC date failed\n");
return ret;
}
/* 设置时间 */
ret = set_time(17, 43, 00);
if (ret != RT_EOK)
{
rt_kprintf("set RTC time failed\n");
return ret;
}
/* 获取时间 */
now = time(RT_NULL);
rt_kprintf("%s\n", ctime(&now));
/* 延时1秒 */
rt_thread_mdelay(1000);
/* 获取时间 */
now = time(RT_NULL);
rt_kprintf("%s\n", ctime(&now));
return ret;
}
void user_alarm_callback(rt_alarm_t alarm, time_t timestamp)
{
rt_kprintf("user alarm callback function.\n");
}
void alarm_sample(void)
{
struct rt_alarm_setup setup;
struct rt_alarm * alarm = RT_NULL;
static time_t now;
struct tm p_tm;
if (alarm != RT_NULL)
return;
/* 获取当前时间戳,并把下5秒时间设置为闹钟时间 */
now = time(NULL) + 5;
gmtime_r(&now,&p_tm);
setup.flag = RT_ALARM_ONESHOT;
setup.wktime.tm_year = p_tm.tm_year;
setup.wktime.tm_mon = p_tm.tm_mon;
setup.wktime.tm_mday = p_tm.tm_mday;
setup.wktime.tm_wday = p_tm.tm_wday;
setup.wktime.tm_hour = p_tm.tm_hour;
setup.wktime.tm_min = p_tm.tm_min;
setup.wktime.tm_sec = p_tm.tm_sec;
alarm = rt_alarm_create(user_alarm_callback, &setup);
if(RT_NULL != alarm)
{
rt_alarm_start(alarm);
}
}
/* export msh cmd */
MSH_CMD_EXPORT(alarm_sample,alarm sample);
```
4. 测试效果(利用FINSH进行调试)
- 2024-03-03
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 12 GCC Makefile 工程模板建立与程序下载
本帖最后由 みずじ 于 2024-3-3 16:24 编辑
## 雅特力车规级MCU-AT32A403A开发板评测 12 GCC Makefile 工程模板建立与程序下载
### 1. 软硬件平台
1. AT32A403A Board开发
2. GCC Makefile
3. Artery_ICP_Programmer_V3.0.13
### 2.编译工具链配置
搭建过程参考 https://gitee.com/End-ING/embedded-gcc-template
#### 1. GNU Arm Embedded Toolchain交叉编译器
进入arm开发者官网,往下滑动选择下载解压可用的ZIP压缩包文件
[下载链接: Downloads | GNU Arm Embedded Toolchain Downloads – Arm Developer](https://gitee.com/link?target=https%3A%2F%2Fdeveloper.arm.com%2Ftools-and-software%2Fopen-source-software%2Fdeveloper-tools%2Fgnu-toolchain%2Fgnu-rm%2Fdownloads)
将下载好的压缩包文件解压在gcc-arm-none-eabi”文件夹中,并记住文件内“bin”文件的路径,后续需添加到系统环境变量Path中。
添加完环境变量后,进行测试,检测是否安装好。
#### 2. 安装Mingw-w64 GCC
点击链接进入到SourceForge官网,往下翻可以找到很多版本的下载链接,选择红色框内型号即可,不同前后缀的具体差异请参考:
[MinGW gcc下载链接及sjlj、dwarf、seh异同以及gcc安装_AMDDMA的博客-CSDN博客_seh和sjlj](https://gitee.com/link?target=https%3A%2F%2Fblog.csdn.net%2FAMDDMA%2Farticle%2Fdetails%2F111600238)
下载链接:
[MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net](https://gitee.com/link?target=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fmingw-w64%2Ffiles%2F)
同样,将下载好的文件解压到“gcc-arm-none-eabi”文件夹下,记住目录下的“bin”文件路径
进入“bin”文件内找到“mingw32-make”应用程序文件,复制一份并重命名为“make”。这么做有利于在命令行执行make指令,而不是输入mingw32-make。
添加完环境变量后,进行测试,检测是否安装好。
#### 3.程序下载软件Artery_ICP_Programmer
(参考上面的教程,应该是使用OpenOCD去下载程序的,但是我下载失败了,目前没有找到解决方法,因此,决定使用其他的方法去下载程序,例如Artery_ICP_Programmer,Artery_ISP_Programmer)
下载地址https://www.arterytek.com/cn/support/index.jsp?index=5
#### 程序下载方法合集
- ICP(In-Circuit Programming)。它让用户不需要将已经安装的 MCU 从目标 PCB 取下就能够通过软 件控制来更新 MCU 的程序存储。
- ISP在线编程(In-System Programming)。具有 ISP 功能的单片机芯片,可以直接在电路板上给芯片写入或者擦除程序。
- UART: 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)。是一种全双工异步通信的串 行通讯端口(COM)。
- USB: 通用串行总线(Universal Serial Bus)。是一种用于规范电脑与外部设备连接与通讯的外部总线标准。
- DFU: (Device Firmware Upgrade)。是一种基于 USB 通讯的设备固件更新协议。
综上所述,基本的软件环境已经配置好。
### 3.Makefile文件编写(重点)
```c
######################################
# target
######################################
TARGET = at32a403a_template
######################################
# building variables
######################################
# debug build?
DEBUG = 1
# optimization for size
OPT = -Os
#######################################
# paths
#######################################
# Build path
BUILD_DIR = build
######################################
# source
######################################
# C sources
C_SOURCES = \
Drivers/cmsis/cm4/device_support/system_at32a403a.c \
Drivers/drivers/src/at32a403a_acc.c \
Drivers/drivers/src/at32a403a_adc.c \
Drivers/drivers/src/at32a403a_bpr.c \
Drivers/drivers/src/at32a403a_can.c \
Drivers/drivers/src/at32a403a_crc.c \
Drivers/drivers/src/at32a403a_crm.c \
Drivers/drivers/src/at32a403a_dac.c \
Drivers/drivers/src/at32a403a_debug.c \
Drivers/drivers/src/at32a403a_dma.c \
Drivers/drivers/src/at32a403a_exint.c \
Drivers/drivers/src/at32a403a_flash.c \
Drivers/drivers/src/at32a403a_gpio.c \
Drivers/drivers/src/at32a403a_i2c.c \
Drivers/drivers/src/at32a403a_misc.c \
Drivers/drivers/src/at32a403a_pwc.c \
Drivers/drivers/src/at32a403a_rtc.c \
Drivers/drivers/src/at32a403a_sdio.c \
Drivers/drivers/src/at32a403a_spi.c \
Drivers/drivers/src/at32a403a_tmr.c \
Drivers/drivers/src/at32a403a_usart.c \
Drivers/drivers/src/at32a403a_usb.c \
Drivers/drivers/src/at32a403a_wdt.c \
Drivers/drivers/src/at32a403a_wwdt.c \
Drivers/drivers/src/at32a403a_xmc.c \
Application/main.c \
Application/at32a403a_int.c \
Application/at32a403a_clock.c \
BspDriver/at32a403a_board.c \
# ASM sources
ASM_SOURCES = \
Application/startup_at32a403a.s \
#######################################
# binaries
#######################################
PREFIX = arm-none-eabi-
GCC_PATH = /SoftwareApplication/gcc-arm-none-eabi/bin
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
#######################################
# CFLAGS
#######################################
# cpu
CPU = -mcpu=cortex-m4
# fpu
FPU = -mfpu=fpv4-sp-d16
# float-abi
FLOAT-ABI = -mfloat-abi=hard
# mcu
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
# macros for gcc
# AS defines
AS_DEFS =
# C defines
C_DEFS = \
-D USE_STDPERIPH_DRIVER \
-D AT32A403AVGT7 \
-D AT_START_A403A_V1 \
# AS includes
AS_INCLUDES =
# C includes
C_INCLUDES = \
-IDrivers/cmsis/cm4/core_support \
-IDrivers/cmsis/cm4/device_support \
-IDrivers/drivers/inc \
-IApplication \
-IBspDriver \
# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS = $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# Generate dependency information
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
#######################################
# LDFLAGS
#######################################
# link script
LDSCRIPT = Application/AT32A403AxG_FLASH.ld
# libraries
LIBS = -lc -lm -lnosys
LIBDIR =
LDFLAGS = $(MCU) -u_printf_float -specs=nosys.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
# default action: build all
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
# #######################################
# # build the application
# #######################################
# # list of objects
# OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
# vpath %.c $(sort $(dir $(C_SOURCES)))
# # list of ASM program objects
# OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.S=.o)))
# vpath %.S $(sort $(dir $(ASM_SOURCES)))
# $(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
# @echo "[CC] $ $@"
# @$(HEX) $< $@
# $(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
# @echo "[BIN] $< -> $@"
# @$(BIN) $< $@
# $(BUILD_DIR):
# @mkdir $@
#
#######################################
# build the application
#######################################
# list of objects
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
@echo "[CC] $ $@"
@$(HEX) $< $@
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf | $(BUILD_DIR)
@echo "[BIN] $< -> $@"
@$(BIN) $< $@
$(BUILD_DIR):
@mkdir $@
#######################################
# flash 目前无法使用,openocd没有适配好
#######################################
flash:
openocd -f atlink.cfg -f at32f403axx.cfg -c init -c halt -c "program build/$(TARGET).elf verify reset exit"
#######################################
# clean up
#######################################
clean:
-del /q $(BUILD_DIR)
#######################################
# dependencies
#######################################
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
```
#### make 测试
执行make命令,生成hex,bin,elf可执行文件。
### 4.程序下载(hex文件下载)
1. 编写程序
```c
#include "at32a403a_board.h"
#include "at32a403a_clock.h"
__IO uint32_t time_cnt = 0;
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
system_clock_config();
at32_board_init();
uart_print_init(115200);
printf("Hardware_Init [ok] \r\n");
printf("at_start_a403a board testing 2024-03-03 [ok]\r\n");
printf("at_start_a403a board arm-gcc-makefile template [ok] \r\n");
while(1)
{
printf("at32_led_toggle example start [ok] \r\n");
at32_led_toggle(LED2);
delay_ms(200);
at32_led_toggle(LED3);
delay_ms(200);
at32_led_toggle(LED4);
delay_ms(200);
printf("at32_led_toggle example end [ok]\r\n");
printf("usart printf counter: %u\r\n",time_cnt++);
delay_sec(1);
}
}
```
2. gcc下面的串口打印问题,在gcc下面使用_write进行定向printf。
(如果是从KEIL的例程printf打印无法在gcc工程中使用,因此需要修改,雅特力官方提供的工程模板就很好的兼容了两者,不需要我们去进行如何的操作,具体实现在at32a403a_board.c中)
1. 打开Artery_ICP_Programmer,连接AT-Link(J-Link,板载AT-Link)
连接成功后,会显示mcu芯片型号AT32A403AVGT7 存储器东西1024KB,AT-Link固件版本号
2. 添加Hex文件,读取信息
3. 下载程序
设置下载选项,擦除选项为:主存储器全擦除
如果选择了下载后启用访问保护,如果使用其他软件去下载时,会发现无法下载,需要解除访问保护,因此在这里,我们默认不选择,虽然软件会提示你要进行保护。
5. 代码下载成功之后,需要重启开发板
(不知道为什么直接按下复位按键没有用,导致一直认为没有下载成功,最后直接把插在板子上的usb线重新插拔之后,发现程序中的led在闪烁,串口在打印log信息,说明没有问题)
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测11 RTOS移植-FreeRTOS 任务调度管理等应用测试
本帖最后由 みずじ 于 2024-3-3 12:01 编辑
## 雅特力车规级MCU-AT32A403A开发板评测11 RTOS移植-FreeRTOS 任务调度管理等应用测试
### 1. 软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. FreeRTOS源码
### 2. FreeRTOS
在嵌入式领域,嵌入式实时操作系统正得到越来越广泛的应用。采用嵌入式操作系统(RTOS)可以 更合理、更高效的利用CPU的资源,简化应用软件的设计,缩短系统开发时间,更好的保证系统的 实时性和可靠性。
FreeRTOS是一个轻量级的实时操作系统内核。作为一个轻量级的操作系统,功能包括:任务管理、 时间管理、信号量、消息队列、内存管理、记录功能、软件定时器、协程等,可基本满足较小系统的 需求。由于RTOS需要占用一定系统资源(尤其是RAM资源),只有UCOSII/III、embOS、RTT、 FreeRTOS等少数实时操作系统能在小RAM的MCU上运行。相对于UCOSII/III和embOS等商用操作 系统,FreeRTOS是免费开源的操作系统,具有源码公开、可移植、可剪裁、调度策略灵活等特点, 可以方便的移植到MCU上运行。
#### FreeRTOS主要功能和特点如下:
1. 用户可配置内核功能
2. 多平台的支持
3. 提供一个高层次的信任代码的完整性
4. 目标代码小、简单易用
5. 遵循MISRA-C标准编程规范
6. 强大的执行跟踪功能
7. 堆栈溢出检测
8. 没有限制的任务数量和优先级
9. 多个任务可以分配相同的优先权
10. 队列、二进制信号量、计数信号量、递归通信和同步的任务
11. 优先级继承
#### 系统功能
作为一个轻量级的操作系统,功能包括:任务管理、时间管理、信号量、消息队列、内存管理、记录 功能、软件定时器、协程等,可基本满足较小系统的需求。FreeRTOS内核支持优先级调度算法,不 同任务可根据重要程度的不同被赋予一定的优先级,CPU总是让处于任务就绪、优先级最高的任务 运行。FreeRTOS同样支持时间片轮转调度算法,系统允许不同的任务处于同一优先级下,在没有更 高优先级任务就绪的情况下,同一优先级的任务共享系统资源。 FreeRTOS内核可根据需要设置为可剥夺型内核和不可剥夺型内核。当FreeRTOS配置成可剥夺型内 核时,处于就绪态的高优先级任务能剥夺低优先级任务的CPU使用权,这样提高了系统的实时性; 当FreeRTOS配置成不可剥夺型内核时,处于就绪态的高优先级任务只能等当前任务主动释放CPU使 用权才能获得运行,这样提高了系统的运行效率。
### 3. AT32官方 FreeRTOS例程
编译代码,正常运行
### 4. AT32A403A-Board开发板移植
1. 下载FreeRTOS源码
下载地址https://www.freertos.org/zh-cn-cmn-s/a00104.html
在下载页发现有不少关于FreeRTOS内核的文档,后续学习可以参考
查看下载的压缩包,主要需要的是FreeRTOS内核文件,库文件等(Source,include,portable)
2. 重新建立工程模板,添加FreeRTOS文件夹.
3. 添加FreeRTOS源代码到工程中
freertos/core 内核源代码 freertos/port 任务调度算法 宏定义文件 不同芯片平台的接口文件(这里使用的Cortex-M4F)
添加库文件路径
4. 添加FreeRTOSConfig.h配置文件
### 4.编写程序,任务调度管理等应用测试验证
1. 任务创建与调度测试
```c
#include "main.h"
//--------------------------------------------------------------------------------------------------
// 自定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
#define Delete_Task 1 //测试任务删除API
#define Suspend_Resume_Task 0 //测试任务挂起与恢复API
#define NULL 0
//--------------------------------------------------------------------------------------------------
// FreeRTOS相关定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
/******************************************************
* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
/******************************************************
* led1_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
/******************************************************
* led2_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 128
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED2任务函数
void led1_task(void *pvParameters)
{
//uint32_t task1_num =0;
while(1)
{
//printf("task1_num:%d\r\n",++task1_num);
printf("led1task is Running...\r\n");
at32_led_toggle(LED2);
vTaskDelay(500);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
//uint32_t task2_num =0;
while(1)
{
//printf("task2_num:%d\r\n",++task2_num);
printf("led2task is Running... \r\n");
at32_led_toggle(LED3);
vTaskDelay(100);
}
}
```
2. 任务动态创建与删除测试
```c
#include "main.h"
//--------------------------------------------------------------------------------------------------
// 自定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
#define Delete_Task 1 //测试任务删除API
#define Suspend_Resume_Task 0 //测试任务挂起与恢复API
#define NULL 0
//--------------------------------------------------------------------------------------------------
// FreeRTOS相关定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
/******************************************************
* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
/******************************************************
* led1_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
/******************************************************
* led2_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 128
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
/******************************************************
* key_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_STK_SIZE 128
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);
///******************************************************
// * float_task 任务 配置
// * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
// *****************************************************/
////任务优先级
//#define FLOAT_TASK_PRIO 4
////任务堆栈大小
//#define FLOAT_STK_SIZE 50
////任务句柄
//TaskHandle_t FLOATTask_Handler;
////任务函数
//void float_task(void *pvParameters);
/******************************************************
* list_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LIST_TASK_PRIO 5
//任务堆栈大小
#define LIST_STK_SIZE 128
//任务句柄
TaskHandle_t LISTTask_Handler;
//任务函数
void list_task(void *pvParameters);
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
//按键测试任务
xTaskCreate((TaskFunction_t )key_task,
(const char* )"key_task",
(uint16_t )KEY_STK_SIZE,
(void* )NULL,
(UBaseType_t )KEY_TASK_PRIO,
(TaskHandle_t* )&KEYTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED2任务函数
void led1_task(void *pvParameters)
{
//uint32_t task1_num =0;
while(1)
{
//printf("task1_num:%d\r\n",++task1_num);
printf("led1task is Running...\r\n");
at32_led_toggle(LED2);
vTaskDelay(500);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
//uint32_t task2_num =0;
while(1)
{
//printf("task2_num:%d\r\n",++task2_num);
printf("led2task is Running... \r\n");
at32_led_toggle(LED3);
vTaskDelay(100);
}
}
//按键测试任务 判断按键KEY0,按下KEY0删除task1 */
void key_task(void *pvParameters)
{
uint8_t key=0;
#if Delete_Task
while(1)
{
printf("key_task is Running... \r\n");
key = at32_button_press(); //按键扫描
if(key == USER_BUTTON)
{
if(LED1Task_Handler != NULL)
{
printf("delete led1_task... \r\n");
vTaskDelete(LED1Task_Handler);
LED1Task_Handler = NULL;
}
}
vTaskDelay(10);
}
#endif
}
```
3. 任务动态创建与列表项应用测试
```c
#include "main.h"
//--------------------------------------------------------------------------------------------------
// 自定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
#define Delete_Task 1 //测试任务删除API
#define Suspend_Resume_Task 0 //测试任务挂起与恢复API
#define NULL 0
//--------------------------------------------------------------------------------------------------
// FreeRTOS相关定义 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//--------------------------------------------------------------------------------------------------
/******************************************************
* START_TASK 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
/******************************************************
* led1_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED1_TASK_PRIO 2
//任务堆栈大小
#define LED1_STK_SIZE 128
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
/******************************************************
* led2_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LED2_TASK_PRIO 3
//任务堆栈大小
#define LED2_STK_SIZE 128
//任务句柄
TaskHandle_t LED2Task_Handler;
//任务函数
void led2_task(void *pvParameters);
/******************************************************
* key_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define KEY_TASK_PRIO 4
//任务堆栈大小
#define KEY_STK_SIZE 128
//任务句柄
TaskHandle_t KEYTask_Handler;
//任务函数
void key_task(void *pvParameters);
///******************************************************
// * float_task 任务 配置
// * 包括: 任务句柄 任务优先级 堆栈大小 创建任务
// *****************************************************/
////任务优先级
//#define FLOAT_TASK_PRIO 4
////任务堆栈大小
//#define FLOAT_STK_SIZE 50
////任务句柄
//TaskHandle_t FLOATTask_Handler;
////任务函数
//void float_task(void *pvParameters);
/******************************************************
* list_task 任务 配置
* 包括: 任务句柄 任务优先级 堆栈大小 创建任务
*****************************************************/
//任务优先级
#define LIST_TASK_PRIO 5
//任务堆栈大小
#define LIST_STK_SIZE 128
//任务句柄
TaskHandle_t LISTTask_Handler;
//任务函数
void list_task(void *pvParameters);
//开始任务任务函数
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led2_task,
(const char* )"led2_task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
//按键测试任务
// xTaskCreate((TaskFunction_t )key_task,
// (const char* )"key_task",
// (uint16_t )KEY_STK_SIZE,
// (void* )NULL,
// (UBaseType_t )KEY_TASK_PRIO,
// (TaskHandle_t* )&KEYTask_Handler);
//浮点测试任务
// xTaskCreate((TaskFunction_t )float_task,
// (const char* )"float_task",
// (uint16_t )FLOAT_STK_SIZE,
// (void* )NULL,
// (UBaseType_t )FLOAT_TASK_PRIO,
// (TaskHandle_t* )&FLOATTask_Handler);
xTaskCreate((TaskFunction_t )list_task,
(const char* )"list_task",
(uint16_t )LIST_STK_SIZE,
(void* )NULL,
(UBaseType_t )LIST_TASK_PRIO,
(TaskHandle_t* )&LISTTask_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
//LED2任务函数
void led1_task(void *pvParameters)
{
//uint32_t task1_num =0;
while(1)
{
//printf("task1_num:%d\r\n",++task1_num);
printf("led1task is Running...\r\n");
at32_led_toggle(LED2);
vTaskDelay(500);
}
}
//LED2任务函数
void led2_task(void *pvParameters)
{
//uint32_t task2_num =0;
while(1)
{
//printf("task2_num:%d\r\n",++task2_num);
printf("led2task is Running... \r\n");
at32_led_toggle(LED3);
vTaskDelay(100);
}
}
//按键测试任务 判断按键KEY0,按下KEY0删除task1 */
void key_task(void *pvParameters)
{
uint8_t key=0;
#if Delete_Task
while(1)
{
printf("key_task is Running... \r\n");
key = at32_button_press(); //按键扫描
if(key == USER_BUTTON)
{
if(LED1Task_Handler != NULL)
{
printf("delete led1_task... \r\n");
vTaskDelete(LED1Task_Handler);
LED1Task_Handler = NULL;
}
}
vTaskDelay(10);
}
#endif
}
List_t TestList; /* 定义测试列表 */
ListItem_t ListItem1; /* 定义测试列表项1 */
ListItem_t ListItem2; /* 定义测试列表项2 */
ListItem_t ListItem3; /* 定义测试列表项3 */
void list_task(void *pvParameters)
{
vListInitialise(&TestList); /* 初始化列表 */
vListInitialiseItem(&ListItem1); /* 初始化列表项1 */
vListInitialiseItem(&ListItem2); /* 初始化列表项2 */
vListInitialiseItem(&ListItem3); /* 初始化列表项3 */
ListItem1.xItemValue = 40;
ListItem2.xItemValue = 60;
ListItem3.xItemValue = 50;
/* 第二步:打印列表和其他列表项的地址 */
printf("/**************第二步:打印列表和列表项的地址**************/\r\n");
printf("项目\t\t\t地址\r\n");
printf("TestList\t\t0x%p\t\r\n", &TestList);
printf("TestList->pxIndex\t0x%p\t\r\n", TestList.pxIndex);
printf("TestList->xListEnd\t0x%p\t\r\n", (&TestList.xListEnd));
printf("ListItem1\t\t0x%p\t\r\n", &ListItem1);
printf("ListItem2\t\t0x%p\t\r\n", &ListItem2);
printf("ListItem3\t\t0x%p\t\r\n", &ListItem3);
printf("/**************************结束***************************/\r\n");
printf("\r\n/*****************第三步--列表项1插入列表******************/\r\n");
vListInsert((List_t* )&TestList, /* 列表 */
(ListItem_t*)&ListItem1); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第四步:列表项2插入列表 */
printf("\r\n/*****************第四步--列表项2插入列表******************/\r\n");
vListInsert((List_t* )&TestList, /* 列表 */
(ListItem_t*)&ListItem2); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第五步:列表项3插入列表 */
printf("\r\n/*****************第五步--列表项3插入列表******************/\r\n");
vListInsert((List_t* )&TestList, /* 列表 */
(ListItem_t*)&ListItem3); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第六步:移除列表项2 */
printf("\r\n/*******************第六步--移除列表项2********************/\r\n");
uxListRemove((ListItem_t* )&ListItem2); /* 移除列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/**************************结束***************************/\r\n");
/* 第七步:列表末尾添加列表项2 */
printf("\r\n/****************第七步--列表末尾添加列表项2****************/\r\n");
TestList.pxIndex = &ListItem1;
vListInsertEnd((List_t* )&TestList, /* 列表 */
(ListItem_t* )&ListItem2); /* 列表项 */
printf("项目\t\t\t\t地址\r\n");
printf("TestList->pxIndex\t\t0x%p\r\n", TestList.pxIndex);
printf("TestList->xListEnd->pxNext\t0x%p\r\n", (TestList.xListEnd.pxNext));
printf("ListItem1->pxNext\t\t0x%p\r\n", (ListItem1.pxNext));
printf("ListItem2->pxNext\t\t0x%p\r\n", (ListItem2.pxNext));
printf("ListItem3->pxNext\t\t0x%p\r\n", (ListItem3.pxNext));
printf("TestList->xListEnd->pxPrevious\t0x%p\r\n", (TestList.xListEnd.pxPrevious));
printf("ListItem1->pxPrevious\t\t0x%p\r\n", (ListItem1.pxPrevious));
printf("ListItem2->pxPrevious\t\t0x%p\r\n", (ListItem2.pxPrevious));
printf("ListItem3->pxPrevious\t\t0x%p\r\n", (ListItem3.pxPrevious));
printf("/************************实验结束***************************/\r\n");
while(1)
{
vTaskDelay(1000);
}
}
```
测试效果
1. 主函数
```c
#include "main.h"
void FreeRTOS_demo(void)
{
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
system_clock_config();
at32_board_init();
uart_print_init(115200);
printf("Hardware_Init [ok] \r\n");
printf("at_start_a403a board testing 2024-03-03\r\n");
printf("at_start_a403a board rtos-freertos \r\n");
printf("freertos Task creation (dynamic) [ok] \r\n");
printf("freertos Task creation (dynamic) and deletion [ok] \r\n");
printf("Lists and list items, insertion and deletion of list item [ok] \r\n");
FreeRTOS_demo();
while(1)
{
}
}
```
2. 测试效果
2.1 任务动态创建
2.2 任务动态创建与删除
2.3 任务动态创建 列表项应用
- 2024-03-01
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 10 RTOS移植-RT-Thread 线程管理测试
本帖最后由 みずじ 于 2024-3-3 16:26 编辑
### 雅特力车规级MCU-AT32A403A开发板评测 10 RTOS移植-RT-Thread 线程管理测试
[Embedded-ArteryTek32-Board 雅特力科技AT32开发板评测代码合集]( https://gitee.com/End-ING/embedded-arterytek32-board "Embedded-ArteryTek32-Board 雅特力科技AT32")
Embedded-Note开发板评测记录 https://end-ing.gitee.io/embedded-mcu-note-doc/#
### 1. 软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. RT-Thread V4.1版本源码
发行版下载地址 https://gitee.com/rtthread/rt-thread/releases/tag/v4.1.0
### 2. RT-Thread
RT-Thread是一个集实时操作系统(RTOS)内核、中间件组件的物联网操作系统,架构如下:
- 内核层:RT-Thread内核,是 RT-Thread的核心部分,包括了内核系统中对象的实现,例如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等;libcpu/BSP(芯片移植相关文件 / 板级支持包)与硬件密切相关,由外设驱动和 CPU 移植构成。
- 组件与服务层:组件是基于 RT-Thread内核之上的上层软件,例如虚拟文件系统、FinSH命令行界面、网络框架、设备框架等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
### 3. AT32A403A-Board开发板移植
1. 下载RT-Thread V4.1版本源码,寻找at32相关bsp驱动包
2. 复制at32f403a-start,并重新命名为at32a403a-start-board
3. 打开对应的工程project,编译下载程序,发现竟然可以直接使用。
我一直比较好奇at32f403a与at32a403a的区别是啥子,我查看一些文档,里面是芯片外设什么的都是差不多的,甚至我感觉都是一样的,但是我没有证据,但是这一次下载代码之后,发现直接通用,感觉可能是同一个芯片,应该经过了比较严格的认证。
4. 虽然上面的at32f403a-start的工程可以直接使用,但是还是建立一下at32a403a的rt-thread工程吧。(主要是用at32a403a的sdk文件替代at32f403a的文件)
重新建立工程模板,文件架构如下:
| 名称 | 描述 |
| ------------ | ---------------------------------------------- |
| applications | 应用开发程序 |
| build | 编译环节生成中间文件 |
| libraries | cmsis,drivers,rt_drivers驱动库文件 |
| rt_examples | 相关示例代码 |
| rt-thread | rt-thread源码文件夹 |
| components | RT-Thread 的各个组件代码,例如 finsh,gui 等。 |
| include | RT-Thread 内核的头文件。 |
| libcpu | 各类芯片的移植代码。 |
| src | RT-Thread 内核的源文件。 |
5. 打开工程,添加rt-thread源码文件,东西比较多
6. 添加库文件路径,这个添加也比较麻烦
7. 添加board.c rtconfig.h文件,这两个文件非常重要。重点需要修改这两个文件。(由于直接使用at32f403a的工程,则不需要修改)
8. 注释中断文件中的HardFault_Handler、PendSV_Handler、SysTick_Handler函数
9. 编译代码(文件添加可参考工程设置,上面可能写的不清楚,因为小东西太多了)
### 4. 编写程序,线程管理测试验证
1. 线程测试函数
```c
/* * 程序清单:创建、初始化/脱离线程
*
* 这个例子会创建两个线程,一个动态线程,一个静态线程。
* 静态线程在运行完毕后自动被系统脱离,动态线程一直打印计数。
*/
#include
#define THREAD_PRIORITY 19
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
static rt_thread_t tid1 = RT_NULL;
/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{
rt_uint32_t count = 0;
for (count =0 ;count< 20;count++)
{
/* 线程1采用低优先级运行,打印计数值20 */
rt_kprintf("thread1 count: %d\n", count ++);
}
rt_kprintf("thread1 exit\n");
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程2入口 */
static void thread2_entry(void *param)
{
rt_uint32_t count = 0;
/* 线程2拥有较高的优先级,以抢占线程1而获得执行 */
for (count = 0; count < 10 ; count++)
{
/* 线程2打印计数值 */
rt_kprintf("thread2 count: %d\n", count);
}
rt_kprintf("thread2 exit\n");
/* 线程2运行结束后也将自动被系统脱离 */
}
/* 线程示例 */
int thread_sample(void)
{
/* 创建线程1,名称是thread1,入口是thread1_entry*/
tid1 = rt_thread_create("create_hread1",
thread1_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* 初始化线程2,名称是thread2,入口是thread2_entry */
rt_thread_init(&thread2,
"init_thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_sample, thread sample);
```
2. 主函数
```c
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-03-08 shelton first version
*/
#include
#include
#include "board.h"
#include "drv_gpio.h"
#include "rt_thread_example.h"
/* defined the led2 pin: pd13 */
#define LED2_PIN GET_PIN(D, 13)
/* defined the led3 pin: pd14 */
#define LED3_PIN GET_PIN(D, 14)
/* defined the led4 pin: pd15 */
#define LED4_PIN GET_PIN(D, 15)
int main(void)
{
rt_uint32_t speed = 200;
/* set led2 pin mode to output */
rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
/* set led3 pin mode to output */
rt_pin_mode(LED3_PIN, PIN_MODE_OUTPUT);
/* set led4 pin mode to output */
rt_pin_mode(LED4_PIN, PIN_MODE_OUTPUT);
rt_kprintf("at_start_a403a board testing 2024-03-01\r\n");
rt_kprintf("at_start_a403a board module softiic oled \r\n");
rt_kprintf("at_start_a403a board rtos rt-thread v4.10 \r\n");
thread_sample();
while (1)
{
rt_kprintf("rt-thread testing start [ok] \r\n");
rt_pin_write(LED2_PIN, PIN_LOW);
rt_thread_mdelay(speed);
rt_pin_write(LED3_PIN, PIN_LOW);
rt_thread_mdelay(speed);
rt_pin_write(LED4_PIN, PIN_LOW);
rt_thread_mdelay(speed);
rt_pin_write(LED2_PIN, PIN_HIGH);
rt_thread_mdelay(speed);
rt_pin_write(LED3_PIN, PIN_HIGH);
rt_thread_mdelay(speed);
rt_pin_write(LED4_PIN, PIN_HIGH);
rt_thread_mdelay(speed);
rt_kprintf("rt-thread testing end [ok] \r\n");
}
}
```
3. 测试效果
整个程序先执行thread2,因为THREAD_PRIORITY优先级比thread1高,thread2打印完成10次计数值之后,就执行thread1,打印完成20次计数值结束。同时FINSH完美正常使用。
-
回复了主题帖:
【AT32A403A 车规MCU开发板】 移植rt-thread
能否提供一些源代码工程文件
- 2024-02-18
-
回复了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 09 GC9A01 SPI-LCD 圆形屏幕LVGL移植
WZH70246 发表于 2024-2-18 18:00
这是国产的车规MCU吗
嗯,虽然我不知道车规MCU和不同的区别在哪里,感觉和AT32F403系列应该是同套的,可能通过了比较严格的认证,所以才是车规的,不过国内车规比较牛逼还是不多,还是国外大厂的芯片占比大
- 2024-02-17
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 09 GC9A01 SPI-LCD 圆形屏幕LVGL移植
### 雅特力车规级MCU-AT32A403A开发板评测 09 GC9A01 SPI-LCD圆形屏幕LVGL移植
## 硬件平台
1. AT32A403A Board开发板
2. 1.28寸圆形彩色TFT显示屏高清IPS 模块240X240 SPI接口GC9A01
2. LVGL V8.3源码
### 2.LVGL
LVGL(Light and Versatile Graphics Library)是一个免费的开源图形库,提供创建具有易 于使用的图形元素、漂亮的视觉效果和低内存占用的嵌入式 GUI。
LVGL 是一款具有丰富的部件,具备高级图形特性,支持多种输入设备, 多国语言和独立于硬件之外等免费的开源图形库。接下来我们来看一下 LVGL 图形用户库的主要特点:
1. 强大的构建块:按钮、图表、列表、滑块、图像等部件。
2. 具有高级图形属性:具有动画、抗锯齿、不透明度、平滑滚动的高级图形。
3. 支持各种输入设备:如触摸、鼠标、键盘、编码器。
4. 支持多语言:UTF-8 编码。
5. 支持多显示器:它可以同时使用多个 TFT 或者单色显示器。
6. 支持多种样式属性:它具有类 CSS 样式的完全可定制的图形元素。
7. 独立于硬件之外:它与任何微控制器或显示器一起使用。
8. 可扩展性:它能够以小内存运行(最低 64 kB 闪存,16 kB RAM 的 MCU)。
9. 支持操作系统、外部存储器和 GPU(不是必需的)。
10. 具有高级图形效果:可进行单帧缓冲区操作。
11. 纯 C 编写: C 语言编写以获得最大的兼容性。
### 3.快速移植
参考stm32-lvgl网络移植教程,大体流程,下载代码,能够支持LCD显示的工程模板上面,添加显示接口,添加各种文件添加各种文件路径(这个文件路径设置迷得很,之前在使用stm32,或者acm32的时候,设置的路径添加很少,但是同样的路径在我这个at32的工程下面报了很多错误,虽然只要一步步加好路径,绝大多数肯定是能解决的,但是lvgl添加的东西太多了,你如果进行了修改,可能会导致许多莫名其妙的问题 ,这个就很烦人),源代码文件设置的具体细节不说了。每个人的工程习惯不同,搞的东西都不一样。
1. 下载源代码 https://github.com/lvgl/lvgl/tree/release/v8.3
2. 把文件中的 lv_conf_template.h 文件名修改成 lv_conf.h 文件名
3. .打开 lv_conf.h 文件,修改条件编译指令,如下源码所示。
4. 打开 examples 文件夹,除了 porting 文件夹外,用户可以删除其他文件和文件夹
把porting文件夹重新命名为lvgl_driver,并修改文件名称。
5. 准备一个裸机工程,其中包括lcd,touch驱动,能正常使用,建立LVGL,GUIAPP文件夹。其中LVGL文件夹十分重要(移植主体,源代码)
6. 创建lvgl/src,lvgl/config/,lvgl/port,lvgl/app,把源代码文件全部添加进去,添加文件路径(这个如果出现错误,尽量把所有的文件都,添加路径,)
7. lvgl 的 1ms 心跳 通过调用定时器
```c
crm_clocks_freq_type crm_clocks_freq_struct = {0};
void TMR1_OVF_TMR10_IRQHandler(void)
{
if(tmr_flag_get(TMR1, TMR_OVF_FLAG) != RESET)
{
/* add user code... */
lv_tick_inc(1);//lvgl的1ms中断
tmr_flag_clear(TMR1, TMR_OVF_FLAG);
}
}
void timer_init(void)
{
crm_clocks_freq_get(&crm_clocks_freq_struct);
/* enable tmr1 clock */
crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);
/* tmr1 configuration */
/* time base configuration */
/* systemclock/20000/10000 = 1hz */
tmr_base_init(TMR1, 9, (crm_clocks_freq_struct.ahb_freq / 10000) - 1); //1ms
tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
/* overflow interrupt enable */
tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE);
/* tmr1 overflow interrupt nvic init */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 0);
/* enable tmr1 */
tmr_counter_enable(TMR1, TRUE);
}
```
8. 修改disp代码,显示驱动接口代码(理论上有3个接口文件需要修改,但是目前只有显示接口文件,屏幕不支持触摸,也没有其他的外部设备,因此只需要改显示接口代码)
主要是添加lcd的显示驱动代码,完成初始化,画点函数的配置
```c
void GUI_DrawPoint(uint16_t x,uint16_t y,uint16_t color)
{
LCD_SetCursor(x,y);//设置光标位置
Lcd_WriteData_16Bit(color);
}
```
9. 注意:移植 LVGL 必须开启 C99 模式(要不然很多的报错)
上面只是简单的移植过程,许多东西可以参考正点原子或者韦东山LVGL教程,有错误通过网络搜索解决.
10. 移植过程中,发现栈空间小了,屏幕显示存在有问题,修改了大小
修改前
修改后
### 4.移植测试效果
1. 主函数
```c
#include "main.h"
/** @addtogroup AT32A403A_periph_examples
* @{
*/
/** @addtogroup 403A_USART_printf USART_printf
* @{
*/
//__IO uint32_t time_cnt = 0;
static void lv_ex_label_1(void)
{
/* Create a screen */
lv_obj_t * scr = lv_obj_create(NULL);
lv_scr_load(scr);
lv_obj_set_style_bg_color(scr,lv_palette_main(LV_PALETTE_PURPLE),0);
lv_obj_align(scr,LV_ALIGN_CENTER,0,0);
/* Create a button */
lv_obj_t * btn = lv_btn_create(scr);
lv_obj_align(btn, NULL, LV_ALIGN_CENTER, 40);
/* Create a label for the button */
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Hello EEWORLD !");
/* Create a button */
lv_obj_t * btn1 = lv_btn_create(scr);
lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 90);
/* Create a label for the button */
lv_obj_t * label1 = lv_label_create(btn1);
lv_label_set_text(label1, "By End 2024.02.17!");
/* Create a button */
lv_obj_t * btn2 = lv_btn_create(scr);
lv_obj_align(btn2, NULL, LV_ALIGN_CENTER, 140);
/* Create a label for the button */
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2, "AT32A403A-Board LVGL-Demo");
}
crm_clocks_freq_type crm_clocks_freq_struct = {0};
void TMR1_OVF_TMR10_IRQHandler(void)
{
if(tmr_flag_get(TMR1, TMR_OVF_FLAG) != RESET)
{
/* add user code... */
lv_tick_inc(1);//lvgl的1ms中断
tmr_flag_clear(TMR1, TMR_OVF_FLAG);
}
}
void timer_init(void)
{
crm_clocks_freq_get(&crm_clocks_freq_struct);
/* enable tmr1 clock */
crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);
/* tmr1 configuration */
/* time base configuration */
/* systemclock/20000/10000 = 1hz */
tmr_base_init(TMR1, 9, (crm_clocks_freq_struct.ahb_freq / 10000) - 1); //1ms
tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
/* overflow interrupt enable */
tmr_interrupt_enable(TMR1, TMR_OVF_INT, TRUE);
/* tmr1 overflow interrupt nvic init */
nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
nvic_irq_enable(TMR1_OVF_TMR10_IRQn, 0, 0);
/* enable tmr1 */
tmr_counter_enable(TMR1, TRUE);
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
// unsigned char count_num;
system_clock_config();
at32_board_init();
uart_print_init(115200);
module_smg_gpio_iint();
timer_init();
printf("Hardware_Init [ok] \r\n");
printf("at_start_a403a board testing 2024-02-17\r\n");
printf("at_start_a403a board module softspi lcd lvgl-v8.3 \r\n");
lv_init(); // lvgl系统初始化
lv_port_disp_init(); // lvgl显示接口初始化,放在lv_init()的后面
lv_ex_label_1();
//lv_example_chart_3();
while(1)
{
lv_task_handler(); // lvgl的事务处理
}
}
```
2. 测试效果
- 2024-02-16
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 08 传感器模块测试 DHT11温湿度传感器
### 08 雅特力AT32A403开发板评测 08 传感器模块测试 DHT11温湿度传感器
## 1.软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. 0.96寸 IIC接口 OLED显示模块
4. 传感器模块 DHT11温湿度传感器 BH1750光照强度传感器
### DHT11 温湿度传感器
DHT11是一款有已校准数字信号输出的温湿度传感器。 精度湿度+-5%RH, 温度+-2℃,量程湿度20-90%RH, 温度0~50℃。
更多DHT11信息请参考:https://baike.sogou.com/v73984313.htm?fromTitle=DHT11
下图为DHT11的引脚说明图,DATA引脚为信号输入输出。
DS18B20引脚说明
### DHT11单总线传感器驱动
DHT11 整体工作时序为:主机发送开始信号、DHT11 响应输出、主机接收 40bit 数据(湿度数据+温度数据+校验值),结束信号(可选)。具体过程如下:
1. 总线空闲状态为高电平,主机拉低总线等待 DHT11 响应, 主机把总线拉低必须大于 18ms,保证 DHT11 能检测到起始信号;
2. 主机发送开始信号结束后,拉高总线电平并延时等待 20-40us 后,读取 DHT11 的响应信号;
3. DHT11 接收到主机的开始信号后,等待微处理器开始信号结束,发送 80us 低电平响应信号;
4. DHT11 发送 80us 高电平准备发送数据;
5. DHT11 发送 40bit 数据(湿度数据+温度数据+校验值)。
#### 起始及响应信号
主机拉低总线至少 18ms,然后再拉高总线,延时 20~40us,此时起始信号(有时也叫复位信号)发送完毕。
DHT11 检测到复位信号后,触发一次采样,并拉低总线 80us 表示响应信号,告诉主机数据已经准备好了。DHT11 之后拉高总线 80us,然后开始传输数据。如果检测到响应信号为高电平,则 DHT11 初始化失败,请检查线路是否连接正常。
### 读时序
DHT11 开始传输数据。每 1bit 数据都以 50us 低电平开始,告诉主机开始传输一位数据了。DHT11 以高电平的长短定义数据位是 0 还是 1:当 50us 低电平过后拉高总线,高电平持续 26~28us 表示 0,高电平持续 70us 表示数据 1。
当最后 1bit 数据传送完毕后,DHT11 拉低总线 50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。
位数据0表示方式:
以 50us 低电平开始,高电平持续 26~28us 表示 0。
位数据1表示方式:
以 50us 低电平开始,高电平持续 70us 表示 1。
### 驱动代码
端口初始化
```c
/*
* 函数名:DHT11_GPIO_Config
* 描述 :配置DHT11用到的I/O口
* 输入 :无
* 输出 :无
*/
static void DHT11_GPIO_Config ( void )
{
gpio_init_type gpio_init_struct;
/* enable the gpioa clock */
crm_periph_clock_enable(DHT11_GPIO_CLOCK, TRUE);
/* set default parameter */
gpio_default_para_init(&gpio_init_struct);
/* configure the gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = DHT11_Dout_GPIO_PIN;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(DHT11_Dout_GPIO_PORT, &gpio_init_struct);
}
/*
* 函数名:DHT11_Mode_IPU
* 描述 :使DHT11-DATA引脚变为上拉输入模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_IPU(void)
{
gpio_init_type gpio_init_struct;
/* enable the gpioa clock */
crm_periph_clock_enable(DHT11_GPIO_CLOCK, TRUE);
/* set default parameter */
gpio_default_para_init(&gpio_init_struct);
/* configure the gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
gpio_init_struct.gpio_pins = DHT11_Dout_GPIO_PIN;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(DHT11_Dout_GPIO_PORT, &gpio_init_struct);
}
/*
* 函数名:DHT11_Mode_Out_PP
* 描述 :使DHT11-DATA引脚变为推挽输出模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_Out_PP(void)
{
gpio_init_type gpio_init_struct;
/* enable the gpioa clock */
crm_periph_clock_enable(DHT11_GPIO_CLOCK, TRUE);
/* set default parameter */
gpio_default_para_init(&gpio_init_struct);
/* configure the gpio */
gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
gpio_init_struct.gpio_pins = DHT11_Dout_GPIO_PIN;
gpio_init_struct.gpio_pull = GPIO_PULL_UP;
gpio_init(DHT11_Dout_GPIO_PORT, &gpio_init_struct);
}
```
从DHT11读取一个字节
```c
static uint8_t DHT11_ReadByte ( void )
{
uint8_t i, temp=0;
for(i=0;icheck_sum= DHT11_ReadByte();
/*读取结束,引脚改为输出模式*/
DHT11_Mode_Out_PP();
/*主机拉高*/
DHT11_Dout_1;
/*检查读取的数据是否正确*/
if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
return SUCCESS;
else
return ERROR;
}
else
return ERROR;
}
```
drv_dht11.h
```c
#ifndef __BSP_DHT11_H
#define __BSP_DHT11_H
#include "main.h"
typedef struct
{
uint8_t humi_int; //湿度的整数部分
uint8_t humi_deci; //湿度的小数部分
uint8_t temp_int; //温度的整数部分
uint8_t temp_deci; //温度的小数部分
uint8_t check_sum; //校验和
}DHT11_Data_TypeDef;
#define DHT11_Dout_GPIO_PORT GPIOC
#define DHT11_Dout_GPIO_PIN GPIO_PINS_8
#define DHT11_GPIO_CLOCK CRM_GPIOC_PERIPH_CLOCK
#define DHT11_Dout_0 gpio_bits_write(DHT11_Dout_GPIO_PORT,DHT11_Dout_GPIO_PIN,Bit_RESET)
#define DHT11_Dout_1 gpio_bits_write(DHT11_Dout_GPIO_PORT,DHT11_Dout_GPIO_PIN,Bit_SET )
#define DHT11_Dout_IN() gpio_input_data_bit_read( DHT11_Dout_GPIO_PORT, DHT11_Dout_GPIO_PIN )
static void DHT11_GPIO_Config ( void );
void DHT11_Init ( void );
uint8_t DHT11_Read_TempAndHumidity ( DHT11_Data_TypeDef * DHT11_Data );
#endif
```
### 案例测试
主函数
```c
#include "main.h"
void OLED_Show_Example(void)
{
OLED_ShowString_08x16(00,0,"AT32 AHT10 OLED");
OLED_ShowString_08x16(0,2,"Time:2024.02.16");
OLED_ShowString_06x08(0,4,"Temp:");
OLED_ShowString_06x08(70,4,"Humi:");
OLED_ShowString_06x08(48,4,"^C");
OLED_ShowString_06x08(116,4,"%");
OLED_ShowString_06x08(0,5,"Light:");
OLED_ShowString_06x08(100,5,"lux");
OLED_ShowCHinese(0,6,13);
OLED_ShowCHinese(16,6,14);
OLED_ShowCHinese(32,6,15);
OLED_ShowCHinese(48,6,16);
OLED_ShowCHinese(64,6,17);
}
void Hardware_Iint(void)
{
system_clock_config();
at32_board_init();
uart_print_init(115200);
printf("Hardware_Init start [ok]\r\n");
module_smg_gpio_iint();
printf("module_smg_gpio_iint [ok]\r\n");
OLED_Init();
printf("OLED_Init [ok]\r\n");
DHT11_Init();
printf("DHT11_Init [ok]\r\n");
BH1750_Init();
printf("BH1750_Init [ok]\r\n");
printf("Hardware_Init end [ok] \r\n");
}
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
int8_t Temperature;
int8_t Humidity;
float Light,aht_temp,aht_humi;
int8_t aht_err;
DHT11_Data_TypeDef DHT11_Data;
Hardware_Iint();
printf("at_start_a403a board testing 2024-02-16\r\n");
printf("at_start_a403a board module aht10-oled \r\n");
OLED_Show_Example();
while(1)
{
if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == SUCCESS)
{
Temperature = DHT11_Data.temp_int;
Humidity = DHT11_Data.humi_int;
}
else
{
printf("Read DHT11 ERROR!\r\n");
}
printf("temperature=%d,humidity= %d \r\n" ,Temperature,Humidity);
delay_ms(500);
if (!i2c_CheckDevice(BH1750_Addr))
{
Light = LIght_Intensity();
}
printf("Light_Value:%.1fLx\r\n",Light);
delay_ms(500);
OLED_ShowNumber_UnsignedInteger_06x08(32,4,Temperature,2);
OLED_ShowNumber_UnsignedInteger_06x08(100,4,Humidity,2);
OLED_ShowNumber_Float_06x08(40,5,Light,5,1);
}
}
```
测试效果
-
发表了主题帖:
雅特力车规级MCU-AT32A403A开发板评测 07 传感器模块测试 BH1750光照强度传感器
本帖最后由 みずじ 于 2024-2-16 16:16 编辑
### 07 雅特力AT32A403开发板评测 07 传感器模块测试 BH1750光照强度传感器
## 1.软硬件平台
1. AT32A403A Board开发板
2. MDK-ARM Keil
3. 0.96寸 IIC接口 OLED显示模块
4. 传感器模块 DHT11温湿度传感器 BH1750光照强度传感器
### AT32 传感器模块测试
原计划进行AHT10,BH1750,BMP280,三个不同类型的传感器进行测试,找到传感器之后,先使用STM32去验证了传感器的好坏,因为之前实验过了, 最后发现BMP280传感器可能坏了,因此需要删除了驱动移植,后面想着加一些其他的传感器。
### 2.BH1750光照强度传感器
BH1750FVI 是一款用于 I2C 总线接口的数字环境光传感器 IC。该IC最适合获得环境光,用于调整手机LCD和Keypad背光功率的数据。(传感器原理参考网络)
- IIC总线接口
- 光谱责任近似于人眼反应
- 照度数字转换器
- 宽范围和高分辨率。 ( 1 - 65535 lx )
- 通过断电功能实现低电流
- 50Hz / 60Hz 光噪声抑制功能
- 1.8V 逻辑输入接口
- 无需任何外部零件
- 光源依赖性小。 (例如白炽灯、荧光灯、卤素灯、白光 LED、太阳灯)
- 可以选择 2 种 I2C 从地址。
- 光学窗口影响的可调测量结果(使用此功能可以检测最小 0.11 lx,最大 100000 lx。)
- 小的测量变化 (+/- 20%)
- 红外线的影响很小。
引脚定义:
| 引脚号 | 名称 | 说明 |
| :----- | :--- | :----------------------------------------------------------- |
| 1 | VCC | 供电电压源正极 |
| 2 | SCL | IIC时钟线,时钟输入引脚,由MCU输出时钟 |
| 3 | SDA | IIC数据线,双向IO口,用来传输数据 |
| 4 | ADDR | IIC地址线,接GND时器件地址为0100011 ,接VCC时器件地址为1011100 |
| 5 | GND | 供电电压源负极 |
### 3.BH1750 驱动代码
drv_bh1750.c
```c
#include "main.h"
/*
应用说明:
在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO
*/
static void I2C_BH1750_GPIOConfig(void);
//==================================================================================================
// 函数功能: IIC 外设驱动函数部分
// 函数标记: i2c_Delay
// 函数说明: I2C总线位延迟,最快400KHz
// 形 参:无
// 返 回 值: 无
//-------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
static void i2c_Delay(void)
{
uint8_t i;
/*
下面的时间是通过逻辑分析仪测试得到的。
工作条件:CPU主频72MHz ,MDK编译环境,1级优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
*/
for (i = 0; i < 10; i++);
}
//==================================================================================================
// 函数功能: IIC 外设驱动函数部分
// 函数标记: i2c_Start
// 函数说明: CPU发起I2C总线启动信号
// 形 参:无
// 返 回 值: 无
//-------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
BH1750_I2C_SDA_1();
BH1750_I2C_SCL_1();
i2c_Delay();
BH1750_I2C_SDA_0();
i2c_Delay();
BH1750_I2C_SCL_0();
i2c_Delay();
}
//==================================================================================================
// 函数功能: IIC 外设驱动函数部分
// 函数标记: i2c_Stop
// 函数说明: CPU发起I2C总线停止信号
// 形 参:无
// 返 回 值: 无
//-------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
BH1750_I2C_SDA_0();
BH1750_I2C_SCL_1();
i2c_Delay();
BH1750_I2C_SDA_1();
}
//==================================================================================================
// 函数功能: IIC 外设驱动函数部分
// 函数标记: i2c_SendByte
// 函数说明: CPU发起I2C总线停止信号
// 形 参:_ucByte : 等待发送的字节
// 返 回 值: 无
//-------------------------------------------------------------------------------------------------
// | - | - | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
//==================================================================================================
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
BH1750_I2C_SDA_1();
}
else
{
BH1750_I2C_SDA_0();
}
i2c_Delay();
BH1750_I2C_SCL_1();
i2c_Delay();
BH1750_I2C_SCL_0();
if (i == 7)
{
BH1750_I2C_SDA_1(); // 释放总线
}
_ucByte
-
加入了学习《AT32F425 AHT10温湿度计》,观看 AT32F425 AHT10温湿度计