嵌入式系统原理与开发教程习题答案_第1页
嵌入式系统原理与开发教程习题答案_第2页
嵌入式系统原理与开发教程习题答案_第3页
嵌入式系统原理与开发教程习题答案_第4页
嵌入式系统原理与开发教程习题答案_第5页
已阅读5页,还剩24页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第1章嵌入式基础知识

赖树明

2023/3/24

1.1思政作业:阅读嵌入式系统发展历史文献,了解嵌入式系统的发展历程及其对社会进步和科技发展

的贡献,撰写一篇阅读报告,其中报告格式不限,字数不少于800字。

答案:省略

说明:本题是开放作业,自行发挥。

1.2简述嵌入式系统国内定义及国际定义。

答案:

国际定义:用于控制、监视或者辅助操作机器和设备的装置。该定义是从应用上考虑的,嵌入式系统

是软件和硬件的综合体,还可以涵盖机电等附属装置,只要是一个专用的工控系统都可以认为是嵌入式系

统。

国内定义:在国内,将嵌入式系统的一般定义是:是以应用为中心,通过软硬件裁减,适应对功能、

可靠性、成本、体积、功耗等要求的专用计算机系统。

1.3简述ARM公司与芯片厂家的关系。

答案:

ARM公司本身不进行具体芯片生产,它只负责设计处理器的构架,即处理器最核心的部件,然后把这

个核心部件技术授权给全球各大半导体厂家,芯片厂家在这个核心部件基础上增加上外围设备设计成具有

特定功能的处理器,再卖给设备厂家.

1.4简述STM32F40X最小系统包括哪几部分电路。

答案:

在结构上由片内/片上外设、板上外设构成,在电路上由芯片、电源电路、复位电路、振荡电路、启动

电路组成。

1.5举例描述嵌入式系统的应用领域。

答案:

嵌入式系统几乎包括了生活中的所有电器设备,如掌上PDA、移动工控设备、电视机顶盒、手机、数

字电视、多媒体、汽车导航仪、微波炉、数字相机、电梯控制器、空调控制器、安全控制系统、自动售货

机、消费电子设备、工业仪表与医疗仪器等。

1.6总结冯•诺依曼体系结构和哈佛结构的特点。

答案:

冯・诺依曼结构:把程序本身当作数据来对待,程序和该程序处理的数据用同样的方式存储,该结构的

主要特点:单一存储、统一编址、分时复用。

哈佛结构:

使用两个独立的存储器模块,分别存储指令和数据,每个存储模块都不允许指令和数据并存;使用独

立的两条总线,分别作为CPU与每个存储器之间的专用通信路径,而这两条总线之间毫无关联,也就是该

结构分开存储、独立编址、两倍带宽、效率更高。

第2章Keil开发环境及调试方法

2.1完成STM32F4的KEILARMMDK5开发集成开发环境的安装。

答案:省略

说明:自选根据书本介绍,动手实际安装操作。

2.2使用MDK5创建一个基本STM32F407工程,并且编写一个基本的main函数。

答案:省略

说明:请根据STM32F407软件开发环境搭建.pdf步骤完成。

2.3扩展:请自选查阅资料,理解CMSIS的含义及常用的规范说明。

答案:

CMSIS(CortexMicrocontrollerSoftwareInterfaceStandard),翻译过来是ARMCortex™微控制器

软件接口标准。这个标准是ARM跟芯片厂商共同提出的,是为了不同芯片厂商生产的Cortex-M0/M3/M4

芯片能在软件上基本兼容,各芯片厂商只需要按照该标准实现自家芯片外设驱动程序,就是兼容CMSIS标

准的。CMSIS在规范内容很多,比如系统函数的命名、芯片初始化启动流程等,如STM32的标准库、HAL

库、LL库驱动程序就是根据该标准写的。关于CMSIS官方设备的框架结构如下图所示:

CMSISBuild

第3章STM32F40X外设原理及控制方法

3.1使用寄存器配置方式,如何使能端口F的硬件?

答案:

查询芯片手册的外设时钟寄存器可以知道F口在AHB1ENR寄存器的第5位,

6.3.12RCCAHB1外设时钟使能寄存器(RCC_AHB1ENR)

RCCAHB1peripheralclockenableregister

偏移地址:0x30

复位值:0x00100000

访问:无等待周期,按字、半字和字节访问。

31302928272625242322212019181716

ETHMA

OTGHSOTGHSETHMAETHMAETHMACCMDATABKPSR

Reser­CPTPEDMA2ENDMA1ENRes.

ULPIENENCRXENCTXENCENReservedRAMENAMENReserved

vedN

rwrwrwrwrwrwrwrwrw

1514131211109876543210

GPIOIEGPIOHGPIOGEGPIOFEGPIODGPIOCGPIOBGPIOA

CRCENGPIOEEN

ReservedReservedNENNNENENENEN

rwrwrwrwrwrwrwrwrwrw

因此,只需要把RCC_AHB1ENR寄存器第5位设置为1即可:RCC->AHB1ENR|=1«5;

3.2使用寄存器配置方式,如何配置PF10为输出模式、PE13为输入模式?

答案:

〃开GGPF口时钟

RCC->AHB1ENR|=1«5;

//配置GPF10:01通用输出模式

GPIOF->MODER&=~(3«10*2);〃对应的两位分别清0

GPIOF->MODER|=(1«10*2);〃对应的两位分别设置为01

〃配置默认输出高电平

GPIOF->ODR|=1«10;

〃开GPE,口时钟

RCC->AHB1ENR|=1«4;

〃配置GPF10:00输入模式

GPIOE->MODER&=~(3«13*2);〃对应的两位分别清0

〃配置为:01:上拉-可选

GPIOE->PUPDR&=~(3«13*2);//对应的两位分别清0

GPIOE->PUPDR|=(1«13*2);〃对应的两位分别设置为01

3.3使用寄存器配置方式,编写引脚电平输出控制函数。

答案:

*函数名:gpio__x_output_ctrl

*功能描述:控制指定引脚输出电平

*作<:ZhifaChen

*参数说明:gpio_obj:GPIO地址,如GPIOA,GPIOA,...

*pin_num:本组引脚号;value:l或0

*返回值:无

*修改记录:无

*其他:

voidgpio_x_output_ctrl(GPIO_TypeDef*gpio_obj,intpin_num,intvalue)

(

if(value)

(

gpio_obj->ODR|=(1«pin_num);〃输出高电平

)

else

{

gpio_obj->ODR&=~(1«pin_num);〃输出低电平

)

)

3.4使用寄存器配置方式,编写读取输入引脚电平的函数。

答案:

*函数名:gpio_x_read_pin

*功能描述:读取指定GPIO引脚电平

*作者:ZhifaChen

*参数说明:gpio_obj:GPIO地址,$nGPIOA,GPIOA,……;

*pin_num:本组引脚号;

*返回值:1:高电平,0低电平

*修改记录:无

*其他:

intgpio_x_read_pin(GPIO_TypeDef*gpio_obj,intpin_num)

(

//!!是把非1变成1,0还是0,因为一个引脚值一定是0或1

return!!(gpio_obj->IDR&(1«pin_num));

3.5使用寄存器配置方式,根据图3.80原理图,实现跑马灯功能。

ui

图3.80LED电路图

答案见:homework-ch3-3.51-leds工程

3.6使用寄存器配置方式,根据图3.81,实现按键控制灯的亮、灭,每个按键控制一个LED灯,实现

按一下亮、再按一下灭,如此循环。

图3.81LED电路图

答案见:homework-ch3-3.6-button-leds工程

3.7结合图3.80和图3.81,编程实现4个按键中断,正确控制LED1~LED4的亮、灭。

答案见:homework-ch3-3.7-button-leds-interrupt工程

3.8编程实现三个按键中断不同的抢占优先级,且分另U控制LEDLLED3,它们中断抢占优先级分别设

置如下:

按键1:抢占优先级1

按键2:抢占优先级2

按键3:抢占优先级3

①若轮流按下按键3>按键2>按键1,请说明LEDLLED3的亮、灭现象。

②若轮流按下按键1>按键2>按键3,请说明LEDLLED3的亮、灭现象。

答案见:homework-ch3-3.8-button-leds-inlerrupl-nest工程

说明:

1.代码中在中断服务程序是人为加入1.5S的延时,是为了方便模拟中断执行没完时按键其他按键可以

看到是否能嵌套中断的效果。

2.现象是按下KEY1,在1.5S内按键KEY2或KEY3,都不会马上响应,而是等待KEY1中断程序执

行完成,会响应KEY2,KEY3,按下的中断程序,也就是低优先级不能嵌套高优先级。

反之,按下KEY3,在1.5S内按键K2或K1,会马上响应,而是不需要等待KEY3中断程序执行完成,

就响应KEY2/KEY1的中断程序,会马上响应KEY1,KEY2,按下的中断程序,也就是高优先级可以嵌套

高优先级。

其他顺序分析方法如上面一样,请自选写。

3.9描述串口数据帧中的起始位、数据位、停止位的作用。

起始位:一帧数据中,用1个低电平位来表示通信的开始;

数据位:长度是5~8位,通常是一个字节有效数据,用来存放一帧要传输的有效数据。

停止位:传输一帧数据后停止1位或1.5位传输的时间长度,用于消除异步通信会产生的时间积累误差,

或者说重新同步新的一帧数据传输,这样才可以确保长时间通信数据传输的正确性。

3.10编程实现串口中断,正确控制LED1~LED4的亮、灭,要求如下:

①接收到数据0x00,贝(]LED1点亮;接收到数据OxFO,贝!ILED1熄灭!

②接收到数据0x01,贝IJLED2点亮;接收到数据OxFl,贝!JLED2熄灭!

③接收到数据0x02,贝IJLED3点亮;接收到数据0xF2,贝!ILED3熄灭!

④接收到数据0x03,贝ljLED4点亮;接收到数据0xF3,贝ljLED4熄灭!

答案见:homework-ch3-3.9-leds-uart工程

3.11描述12c的起始信号、应答信号、停止信号的细节。

答案:

起始信号:SCL和SDA初始化为高电平,开始通信时SCL高电平期间SDA由高变成低,其作用相当

于总线复位,通知所有12c从机准备开始通信了。

应答信号:在每个字节传输后紧跟着一个应答位,即第9个时钟SCL高电平期间,SDA是低电平表示

应答信号,即告诉发送方数据已经成功接收。

停止信号:当主机不想通信了,停止通信的条件是SCL高电平期间SDA由低电平变成高电平,其作用

是终止通信。

3.12根据课本12c时序图,编写代码,实现向AT24c02的连续写入与连续读取。

答案见:homework-ch3-3.12-at24c02工程

3.13结合DMA和UART章节内容,编写代码,实现通过DMA方式发送数据。

答案见:homework-ch3-3.13-uart-dma-send工程

3.14综合应用:结合定时器、UART实现程序运行时间统计并且存储,以及通过UART与上位机通

信修改存储EEPROM中运行时间。

答案见:homework-ch3-3.14-runtime-save-eeprom工程

分析:

1.在EEPROM读写工程基本上增加上定时器示例代码

2.记录机器运行时间,需要考虑新机器第一次运行时是没有正确的时间记录的,而EEPROM中的数值

是无法确定的,因此程序设计需要能识别当前是否是已经设置过正确的运行时间,如果没有设置过运行时

间,则表示是新机器,此时设置运行时间为0,更新到EEPROM中。解决方法是在EERPOM中增加一个初

始化的标志值,开机先读取标志位置,判断是否是正确的初始化标志值,如果是,则表示不是新机器,读

取此时的运行时间做当前起始时间,否则运行时间设置为Oo

为方便管理运行时间和已初始化标志,我们把他们设定为一个结构体,并且规定初始化标志值,代

码片段如下所示:

#defineRUNTIME_HAVE_INIT_VALUE0x12345678〃已经初始化过的标志值

typedefstructruntime_data

(

u32second;//运行运行时间

u32have_init;〃是否是初始化了

}runtime_data_t;

3.定义时间存放变量和存储地址,代码片段如下所示

#defineRUN_TIME_POS0x10//存放运行参数位置

runtime_data_tg_runtime;//运行运行时间

4.编写更新运行时间和读取时间函数,调用AT24C02实现的读写函数即可

函数名:update_runtime

*功能描述:更新运行时间到EEPROM

作者:ZhifaChen

参数说明:无

返回值:无

修改记录:无

其他:;

voidupdate_runtime(runtime_data_trtime)

(

AT24C02_PageWrite(0xa0,RUN_TIME_POS,(u8*)&rtime,sizeof(runtime_data_t));

)

*函数名:read_runtime

*功能描述:从EEPROM读取运行时间

*作者:ZhifaChen

*参数说明:无

*返回值:无

*修改记录:无

*其他:;

intread_runtime(runtime_data_t*rtime)

(

intret;

ret=AT24C02_ReadBytes(0xa0,RUN_TIME_POS,(u8*)rtime,sizeof(runtime_data_t));

returnret;

5.在定时器中断程序中1秒中增加1,用来做运行时间统计

〃定时器6中断服务程序

voidTIM6_DAC_IRQHandler(void)

(

〃由于定时器6和DAC模块共用一个中断入口地址,因此也共用一个中断服务程序

〃这里先判断是否是属于定时器6产生的中断,如果是再反转LED灯

if(TIM6->SR&(1«0))

(

TIM6->SR&=~(1«0);〃清标志

g_second++;

6.在主程序中每5秒保存一次运行时间到EEPROM

//每10秒保存一次运行时间,每秒保存频率太高,容易损坏EEPROM

if(g_second-g_second_pre>=5)

g_second_pre=g_second;

g_runtime.second+=5;〃当前时间加5秒

update_runtime(g_runtime);〃写入EEPROM,更新时间

7.再根据UART控制LED灯亮灭的示例增加串口命令来修改EEPROM中的运行时间,本示例有两个

清0运行时间和设置运行时间两条指令:

#defineCMD_RUN_TIME_CLR"RTM_CLR"〃清除运行时间

〃设置运行时间命令后面跟时间,单位秒,如RTM_SET123表示设置运行时间是123秒

#defineCMD_RUN_TIME_SET"RTM_SET"

解析代码片段:

//判断命令是否合法,合法,则执行命令

if(!strncmp((char*)uart_recv_buf,CMD_RUN_TIME_CLR,strlen(CMD_RUN_TIME_CLR)))

(

g_runtime.second=0;〃清除运行时间

update_runtime(g_runtime);//写入EEPROM,更新时间

}

elseif(!strncmp((char*)uart_recv_buf,CMD_RUN_TIME_SET,strlen(CMD_RUN_TIME_SET)))

{

〃命令示例:RTM_SET123

g_runtime.second=atoi((char*)&uart_recv_buf[7]);//提示时间值数据

update_runtime(g_runtime);〃写入EEPROM,更新时间

第4章gC/OS-III实时操作系统原理及实践

4.1基于jic/os-ni,编写一个任务,实现跑马灯。

答案:

分析说明:在本节基础ucOS示例代码基础上进行修改,增加LED任务代码即可。

1.把裸机编写的LED代码添加到工作当中

2.在main.c定义新增加的LED任务的相关变量,函数声明,优先级定义

#defineTASK_LED_PRIO6〃任务优先级

#defineTASK_LED_STK_SIZE128〃任务堆栈大小

OS_TCBtask_led_tcb;〃任务控制块

CPU_STKtask」ed_stk[TASK_LED_STK_SIZE];〃任务堆栈

voidtask_led_task(void*p_arg);〃任务函数

3.在start_task函数中增加-一个任务创建,创建led任务

〃创建TaskLed任务

OSTaskCreate((OS_TCB*)&task_led_tcb,〃任务控制块

(CPU.CHAR*)"Taskled'1,〃任务名字

(OS_TASK_PTR)task_led_task,〃任务函数

(void*)0,〃传递给任务函数的参数

(OS_PRIO)TASK_LED_PRIO,//任务优先级

(CPU_STK*)&task」ed_stk⑼,〃任务堆栈基地址

(CPU_STK_SIZE)TASK_LED_STK_SIZE/10,//任务堆栈深度限位

(CPU_STK_SIZE)TASK_LED_STK_SIZE,〃任务堆栈大小

(OS_MSG_QTY)0,//任务内部消息队列能够接收的最大消息数目,为0时禁止接收

消息

(OS_TICK)0,//当使能时间片轮转时的时间片长度,为。时为默认长度,

(void*)0,//用户补充的存储区

(OS_OPT)OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,〃任务选项

(OS_ERR*)&err);〃存放该函数错误时的返回值

4.编写led任务代码,在其中编写流水灯程序

//taskled任务函数

voidtask」ed_task(void*p_arg)

u8taskl_num=0;

OS_ERRerr;

CPU_SR_ALLOC();

p_arg=p_arg;

while(l)

gpio_x_output_ctrl(LED1_GP1O,LEDI_PIN,LED_ON);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);〃延时0.5s

gpio_x_output_ctrl(LED2_GPIO,LED2_PIN,LED_ON);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);〃延时0.5s

gpio_x_output_ctrl(LED3_GPIO,LED3_PIN,LED_ON);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);//延时0.5s

gpio_x_output_ctrl(LED4_GPIO,LED4_PIN,LED_ON);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);//延时0.5s

gpio_x_output_ctrl(LEDl_GPIO,LED1_PIN,LED_OFF);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);//延时0.5s

gpio_x_oulput_ctrl(LED2_GPIO,LED2_PIN,LED_OFF);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);〃延时0.5s

gpio_x_output_ctrl(LED3_GPIO,LED3_PIN,LED_OFF);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_T1ME_HMSM_STRICT,&err);〃延时0.5s

gpio_x_oulput_ctrl(LED4_GPIO,LED4_PIN,LED_OFF);

OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_HMSM_STRICT,&err);〃延时0.5s

完整工程,见:homework-ch4-4.1-ucos-leds

4.2基于jiC/OS-HL编写一个任务管理LED亮、灭,一个任务用于识别按键状态,这两个任务配合实

现按键控制LED的亮、灭。

答案:

分析说明:上一个示例代码基础上进行修改:

1)创建一个消息队列,在按键任务中检测到有按键按下是把按键值放入消息队列中

2)修改LED任务:从消息队列中读取消息,转换成按键值,根据按键值控制不同的LED灯

3)新增加KEY任务:每20ms扫描一次按键状态,如检测到有按键按下发送到消息队列

完整工程,见:homework-ch4-4.2-ucos-button-led

4.3基于jiC/OS-IIL多任务利用互斥锁实现串口发送数据的函数。

答案:

分析说明:裸机的串口多字节发送在任务中使用,有可能在发送数据过程中被高优先级的任务抢占,

而高优先级任务里又调用了串口发送函数,这样就会存在冲突,导致低优先级的数据没有输送完成就被高

优先级任务抢占。解决方法,只需要把串口函数使用互斥信号量进行封装一层即可。

1.定义互斥信号量

OS_MUTEXuart_debug_mutex;//定义一个互斥信号量

2.在start_task任务中创建互斥信号量

〃创建一个互斥信号量

OSMutexCreate((OS_MUTEX*)&uart_debug_mutex,

(CPU_CHAR*)"uart_debug_mutex",

(OS_ERR*)&err);

3.封装串口发送函数

*函数名:os_uart_send_data

*功能描述:加了互斥信号量保保护的串口发送函数,可以防止任务抢占导致的数据混乱

*作者:

*参数说明:无

*返回值:无

*修改记录:无

*其他:;

intos_uart_send_data(u8*data,u16num)

(

OS_ERRerr;

OSMutexPend(&uart_debug_mutex,0,OS_OPT_PEND_BLOCKING,0,&err);//请求互斥信号量

uart_send_bytes(data,num);〃这里是你自己的多字节发送函数,根据实际情况替换即可

OSMutexPost(&uart_debug_mutex,OS_OPT_POST_NONE,&err);〃释放互斥信号量

完整工程,见:homework-ch4-4.3-ucos-mutex-uart

4.4基于HC/OS-IIL串口接收到的数据包通过消息队列发送给任务处理。数据包如下:

点亮LED1:熄灭LED1:

点亮LED2:熄灭LED2:^^led-2-0#^^

点亮LED3:熄灭LED3:^^led-3-0#^^

点亮LED4:熄灭LED4:,,led-4-0#^,

答案:

分析说明:

1.在main.c中定义一个消息队列存放串口消息

〃消息队列定义

#defineUART_MSG_Q_NUM8〃串口消息队列的数量

OS_Quart_data_msg;//定义一个消息队列,

2.在start_task任务中创建消息队列

〃创建消息队列uart_data_msg

OSQCreate((OS_Q*)&uart_data_msg,//消息队列指针

(CPU_CHAR*)"uart_data_msg",〃消息队列名称,自定义

(OS_MSG_QTY)UART_MSG_Q_NUM,〃消息队列长度,

(OS_ERR*)&err);//存放错误码

3.由于串口收到的数据长度可能不相同,收到后是放到消息队列中,延后处理的,因此需要记录实际

数据,为方便处理,我们可以定义一个结构体来存放收到数据长度,收到的数据,然后把该结构存数据包

放入消息队列中,延后处理就可以很方便取出数据长度。

在usart.h中添加以下结构定义:

〃定义串口消息结构,用来往消息队列中存放

typedefstruct{

u8datajen;〃有效数据长度

u8data[128];〃存放数据缓冲区

}uart_data_t;

4.main.c中创建串口处理任务,负责读取队列上的数据,并且进行解析,控制LED,任务函数如下:

〃读取UART队列上的数据来处理,根据处理结果控制LED

voidtask_uart(void*p_arg)

u8taskl_num=0;

OS_ERRerr;

uart_data_t*msg_data;

OS_MSG_SIZEsize;

P_arg=p_arg;

while(l)

〃读取按键任务发来的消息

msg_data=OSQPend((OS_Q*)&uart_data_msg,

(OS.TICK)0,

(OS_OPT)OS_OPT_PEND_BLOCKING,

(OS_MSG_SIZE*)&size,

(CPU_TS*)0,

(OS_ERR*)&err);

if(!stmcmp(nled-l-l#n,(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED1_GPIO,LED1_PIN,LED_ON);

[elseif(!strncmp(nled-l-O#n,(char*)msg_data->data,msg__data->data_len)){

gpio_x_output_ctrl(LEDl_GPIO,LED1_PIN,LED_OFF);

)elseif(!strncmp(,'led-2-l#,,,(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED2_GPIO,LED2_PIN,LED_ON);

Jelseif(!strncmp("led-2-0#H,(char*)msg_data->clata,msg_data->data_len)){

gpio_x_output_ctrl(LED2_GPIO,LED2_PIN,LED_OFF);

(elseif(!strncmp(uled-3-1#",(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED3_GPIO,LED3_PIN,LED_ON);

}elseif(!strncmp(nled-3-0#n,(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED3_GPIO,LED3_PIN,LED_OFF);

[elseif(!strncmp(,'led-4-l#,',(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED4_GPIO,LED4_PIN,LED_ON);

[elseif(!strncmp("led-4-0#',,(char*)msg_data->data,msg_data->data_len)){

gpio_x_output_ctrl(LED4_GPIO,LED4_PIN,LED_OFF);

〃回发给电脑显示收到的结果

os_uart_send_data(msg_data->data,msg_data->data_len);

5.在串口中断服务中,当接收到完整一帧数据后,封装成数据包发送到消息队列上即可,本示例是在

串口的空闲中断位置增加代码,如下:

#defineUART_DATA_ARR_SIZE8//定义8个元素

uart_data_tuart_data_arr[UART_DATA_ARR_SIZE];〃定义全局的消息队列数据包存放空间,

u8uapos_cur=0;〃用来指示当前使用的空闲位置

voidUSART2JRQHandler(void)〃如果你使用UART1就是USARTl」RQHandler

(

.省略

〃位4IDLE:检测到空闲线路(IDLElinedetected)

if(s&1«4)

(

……省略

uart2_recv_buflindex++%UART2_BUF_LEN]廿\(T;〃添加接收字符串的结束标志

index=0;〃清接收计数器

〃复制数据到缓冲区中

memcpy(&uart_data_arr|uapos_cur].data,uart2_recv_buf,uart2_recv_cnt+1);

uart_data_arr[uapos_cur].data_len=uart2_recv_cnt+1;

〃发送当前数据到队列中

externOS_Quart_data_msg;〃声明外部消息队列

u8err;

OSQPost((OS_Q*)&uart_data_msg,

(void*)&uart_data_arr[uapos_cur],

(OS_MSG_SIZE)sizeof(uart_data_t),

(OS_OPT)OS_OPT_POST_FIFO,

(OS_ERR*)&err);

uapos_cur++;

u叩os_cur%=UART_DATA_ARR_SIZE;〃更新下一个存储位置

完整工程,见:homework-ch4-4.4-ucos-uart-leds

第5章FATFS文件系统

5.1移植FATFS至ljSTM32F407ZGT6。

答案:省略

说明:请根书本描述完成文件系统移植操作,此处省略。

5.2使用FATFS的文件操作API实现创建目录、文件复制、删除文件、修改文件内容、重命名文件。

答案:

创建目录

复制文件

#defineREAD_BUF_SIZE1024*4〃定义缓冲区大小

u8cpbuf[READ_BUF_SIZE];//定义缓冲区

u8mf_copy(u8*psrc,u8*pdst)

(

u8retry=0,res;

u16br=0,bw=0;

u8*fbuf=0;

FIL_fsrc,_fdst;

FIL*fsrc=&_fsrc,*fdst=&_fdst;

fbuf=cpbuf;//复制数据临时缓冲区

//打开源文件,如果文件不存在就出错

res=f_open(fsrc,(constTCHAR*)psrc,FA_READ|FA_OPEN_EXISTING);

if(res!=FR_OK)

(

gotoout;

)

//以创建新文件形式打开一个文件,即新文件

res=f_open(fdst,(constTCHAR*)pdst,FA.WRITE|FA_CREATE_ALWAYS);

if(res!=FR_OK)

(

gotoout;

〃循环从源文件中复制数据写入到新文件中

while(res==FR_OK)//开始复制

(

br=0;

res=Lread(fsrc,fbuf,READ_BUF_SIZE,(UINT*)&br);//源头读出4096字节

if(res!=FR_OK)

(

gotoout;

〃文件完成了退出循环

if(!br)

(

break;

res=Lwrite(fdst,ftuf,(UINT)br,(UINT*)&bw);〃写入目的文件

if(res||bw<br)

(

gotoout;

)

)

out:

f_close(fdst);//closefilefdst

f_close(fsrc);//closefilefsrc

returnres;

)

删除文件:调用fLunlink函数即可删除文件

u8res=f_un1ink(file_name);〃删除文件

修改文件:以读写方式打开文件,然后读取内容出来到内存中,修改内存中的数据再回写即可,值得注

意的是,回写数据前应该先fjseek把读写指令调整到文件开头。

#defineREAD_BUF_SIZE1024*4〃定义缓冲区大小

u8cpbuf[READ_BUF_SIZE];〃定义缓冲区

u8modify_file_test(void)

(

u8retry=0,res;

ul6br=0,bw=0;

u8*fbuf=0;

FIL_fsrc,_fdst;

FIL求fsrc=&_fsrc;

fbuf=cpbuf;//复制数据临时缓冲区

#defineFILE_NAME"l.txt1'〃要修改的文件

//打开源文件,如果文件不存在就出错

res=f_open(fsrc.(constTCHAR*)FILE_NAME,FA_READ|FA_WRITE|FA_OPEN_EXISTING);

if(res!=FR_OK)

{

gotoout;

)

//循环从源文件中复制数据写入到新文件中

if(res==FR_OK)//开始复制

br=O;

res=f_read(fsrc,fbuf,READ_BUF_SIZE,(UINT*)&br);//源头读出4096字节

if(res!=FR_OK)

(

gotoout;

〃把修改后的数据重新写入文件

〃这里可以修改已经读取到fbuf中的数据了,以下把源数据6开始数据进行修改

slrcpy((char*)&fbuf[6],“ABCDEFGHIJKLMN\r\n");

res=f_lseek(fsrc,0);〃把文件读写指针移动到文件开头

if(res)

(

gotoout;

)

res=f_write(fsrc,fbuf,(UINT)br,(UINT*)&bw);〃写入目的文件

if(res||bw<br)

(

gotoout;

)

}

out:

f_close(fsrc);//closefilefsrc

returnres;

)

重新命名文件:调用f.rename函数即可修改文件名

u8res=f_rename("oldname.txt","newname.txt”);

5.3编写程序实现接收电脑串口助手发送来的文件,并且保存到SD卡中。

答案:

分析:

1.以本章移植好的文件系统工程{smt32f407-sdio-fatfs-demo2}为基本模板,修改UART接收代码,以及

main.c中增加中增加把UART收到的数据写入到文件中的代码

2.设计思路:文件可能很大,所以UART接收代码需要做一点变动,即收到不能一次性接收完成所有

文件数据再一次性写入,这样对RAM要求很大,解决方法是UART使用两个接收缓冲区,一个用来接收

实时UART字节数据,一个是存放等待写入文件的数据缓冲区。即接收到一定数量,如512字节后就把接

收缓冲区1数据复制到接收缓冲2,并且记录实际数据长度,主程序判断数据长度大于0就把缓冲区2的

数据写入到文件中。那文件发送完成一定是会进入UART的空闲中断里,因此,在空闲中断中设置标志,

标识这时候是文件的最后一笔数据即可。

3.UART代码关键部分:

〃接收数据需要一个缓冲区存放

u8uart1_recv_bufIUART1_BUF_LEN]={0};〃定义接收缓冲区

u8uartl_recv_buf2[UARTl_BUF_LEN]={0};〃定义接收缓冲区2

uint32_tuartl_flag=0;〃接收完成标志

uint32_tuartl_recv_index=0;〃接收下标计数器

uint32_tuartl_recv_cnt=0;〃本次要处理的数字字节数量

//UART中断服务程序

voidUSARTlJRQHandler(void)

(

uint32_ts;

s=USART1->SR;〃读取状态寄存器

〃位5RXNE:读取数据寄存器不为空(Readdataregisternotempty)

〃收到一个数据产生一个中断,读取收到的数据存储到接收缓冲区数组中

if(s&1«5)

(

//清接收中断标志,请DR时候就清中断标志

uartl_recv_buf[uartl_recv_index+4-]=USART1->DR;

USART1->SR&=〜(1«5);〃也可能手动写0清0

〃一次处理512字节数量

if(uart1_recv_index>=512){

uartl_recv_index-=512;〃跳过已经复制的数据

uartl_recv__cnt=512;〃本次复制512字节数据

//复制到处理缓冲区等待写入sd卡

memcpy(uartl_recv_buf2,uartl_recv_buf,uartl_recv_cnt);

)

〃位4IDLE:检测到空闲线路(IDLElinedetected)

if(s&1«4)

(

〃清空闲中断标志:该位由软件序列清零(读入USART_SR寄存器,然后读入USART_DR寄存器)

s=USARTl->SR;〃读取状态寄存器

USART1->DR;〃这样就是读,不接收结果

uartl_recv_bufruartl_recv_index]='\0';〃添加接收字符串的结束标志,可选的

uartl_recv_cnt=uartl_recv_index;〃保存实际字节数量

uartl_recv_index=0;//清接收计数器

uartl_flag=1;//设置中断处理标志

//复制到处理缓冲区等待写入sd卡

memcpy(uarll_recv_buf2,uartl_recv_buf,uartl_recv_cnt);

4.保存数据到文件的函数

*函数gpio_x_read_pin

*功能描述:把数据写入到文件中

*作者:ZhifaChen

*参数说明:fsrc:已经打开的文件结构指针;data:要保存的数据指针;

*num:本组引脚号;

*is_finish:是否是最后一笔数据,1表示最后一笔数据

*返回值:当前写入的累积字节数量;

*修改记录:无

*其他:

u32save_file_test(FIL*fsrc,u8*data,ul6num,u8is_finish)

(

staticu32write_num=0;

u8res=FR_OK;

ul6bw=0;

u32size=write_num;

write_num+=num;〃累加原来写入的字节

res=f_write(fsrc,data,(UINT)num,(UINT*)&bw);〃写入目的文件

if(res||bw<num)

(

Cclose(fsrc);//closefilefsrc

if(is_finish){

write_num=0;〃最后一笔数字写入时清累计值

Lclose(fsrc);//closefilefsrc

returnsize;

}

5.Main函数中关键代码

#defineFILE_NAME_MOD,'uart-data.txtn〃保存的文件名

intmain(void)

(

……原来的代码省略

〃打开源文件,如果文件不存在就出错

res=f_open(&fdst,(constTCHAR*)FILE_NAME_MOD,FA_READ|FA_WRITE|

FA_CREATE_ALWAYS);

if(res!=FR_OK)

(

printf("匚openerror\r\nu);

Delay_ms(1000);

)

while(l)

(

〃如果有数据来对执行写入文件操作

if(uartl_recv_cnt)

(

save_file_test(&fdst,uartl_recv_buf2,uartl_recv_cnt,uartl_flag);

uartl_recv_cnt=0;

if(uartl_flag)

(

uartl_flag=0;〃清接收完成标志

f_close(&fdst);〃关闭文件,这样文件会全部写入sd卡

)

)

6.完整项目工程见homework-ch5-5.3-save-uart-data-to-file

第6章分时操作系统Linux开发环境

6.1安装虚拟机、Ubuntuo

答案:省略

说明:请根据配套完成PPT操作,此处省略。

6.2配置Windows系统共享目录。

答案:省略

说明:请根据配套完成PPT操作,此处省略。

第7章Linux系统命令及Vim使用

7.1在Ubuntu系统中使用命令创建用户(用户名为自己的姓名拼音)并附上详细的创建过程与截图。

答案:

文件(F)编辑(E)查看(V)搜索(S)终端(T)帮助(H)

zhifachen@zhifachen-lx:~$sudouseradd-mxiaoming

[sudo]zhifachen的密码:

zhifachen@zhifachen-lx:-$Is/home/

xiaomingzhifachen

zhifachen@zhifachen-lx:~$sudopasswdxiaoming

输入新的UNIX密码:

重新输入新的UNIX密码:

passwd:已成功更新密码

zhifachenQzhifachen-lx:~$sudousermod-a-Gsudoxiaoming

zhifachen@zhifachen-lx:~$|

创建用户完成后,就可以注销,使用新用户登陆了。

说明:

sudouseradd-mxiaoming:表示创建新用户xiaoming,并且自动生成/home目录下用户家目录

sudopasswdxiaom

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论