0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

实战篇:IO设备

冬至子 来源:编程外星人 作者:怪蛙 2023-10-11 10:13 次阅读

通常我们认为以计算机CPU为核心,其外部的所有的设备都可以称为是外部输入输出设备。例如计算机中的显示器就是一个输出设备,它的作用是将一些数字信号转化为图形信号显示在电子屏幕上,其数据是由内向外流动,因此我们称显示器为输出设备。

又如键盘和鼠标是输入设备,它们的作用是将使用者的命令信号转化为数字信号传递给计算机的CPU,其数据是由外向内流动,因此我们称键盘和鼠标为输入设备。又如,打印机是一个输出设备,游戏手柄和摄像头是输入设备。如下图:

图片

通常的,我们将键盘称为计算机的标准输入设备,将显示器称为计算机的标准输出设备。这也是Unix和类Unix系统中一直延用的名称。我们在做虚拟文件系统时将每一个进程中都分配了一个文件描述结构体数组:

//进程控制块Process Control Block
typedef struct pcb_s
{
  //进程栈顶地址
  void *p_stack;
  //栈内存地址,释放、统计内存时使用
  void *p_stack_mem;
  //栈内在大小
  uint32_t stack_size; 
  //优先级由高0到低32
  uint8_t prio;
  //任务状态
  uint8_t status;
  //任务休眠ticks
  uint32_t sleep_tick;
  //任务入口函数
  void (*task_entry)(void *);
  //任务函数参数
  void *task_arg;
  //进程的文件描位图,1表示空闲,0表示使用
  uint32_t f_use_map;
  //进程的文件描述结构体数组
  vfs_node_s *fnodes[FNODE_SIZE];
} pcb_s;

这个fnodes[FNODE_SIZE]文件描述数组记录了进程所打开每一个设备文件的地址。这个数组的下标就是我们使用open()函数所返回的值,也就是我们通常所说的文件描述符。文件描述符为int类型,范围通常是0~FNODE_SIZE。当文件描述符小于0时表示打开设备文件失败。几乎所有的类Unix系统中都使用了标准输入、标准输出和标准错误这3个IO设备。

所以,操作系统为每一个进程分配文件描述符时,会默认将0、1、2分别用于表示标准输入、标准输出和标准错误这3个设备。所以通常情况下我们使用open()函数来打开一个设备文件时, 返回的文件描述大多数是以3开始的,在同一个进程中同时打开多个设备文件,其文件描述符通常会是3、4、5、6、7、8……等。

实际上,我们在嵌入式领域里的处理器性能有限,外设的各类和功能多种多样,并非像个人电脑或是网络服务器一样通用和统一。在单片机领域中我们所使用的操作系统并非一定要遵循Unix标准或习惯,但为了学习其优秀的设计理念,我们可以将我们的嵌入式操作系统中实现标准输入、标准输出和标准错误这3个设备。

在Cortex-M3处理器中我们可以使用串口设备作为操作系统中的标准输入和标准输出,而从本质上讲标准错误这个设备的功能跟标准输出是一样的,只不过其显示的内容都是程序错误,我们不单独来实现标准错误设备,而只来完成标准输入和标准输出这两个设备。例如串口1的初始化、读、写程序如下:

void serial1_init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  USART_InitStructure.USART_BaudRate = SERIAL_BAUTRATE;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART1- >CR1 |= (USART_CR1_RE | USART_CR1_TE);
  USART_Init(USART1, &USART_InitStructure);
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  USART_Cmd(USART1, ENABLE);
}


void serial1_write(uint8_t data)
{
  uint8_t next_head = serial1_tx_buffer_head + 1;
  if (next_head >= TX_RING_BUFFER1)
  {
    next_head = 0;
  }
  while (next_head == serial1_tx_buffer_tail);
  serial1_tx_buffer[serial1_tx_buffer_head] = data;
  serial1_tx_buffer_head = next_head;
  USART1- >CR1 |= USART_FLAG_TXE;
}


int serial1_read(uint8_t *ch)
{
  uint8_t tail = serial1_rx_buffer_tail;
  if (serial1_rx_buffer_head == tail)
  {
    return 0;
  }
  else
  {
    uint8_t data = serial1_rx_buffer[tail];
    tail++;
    if (tail >= RX_RING_BUFFER1)
    {
      tail = 0;
    }
    serial1_rx_buffer_tail = tail;
    *ch = data;
    return 1;
  }
}


void storeHandleDataIn(uint8_t data)
{
  uint8_t next_head;  
  next_head = serial1_rx_buffer_head + 1;
  if (next_head >= RX_RING_BUFFER1)
  {
    next_head = 0;
  }
    if (next_head != serial1_rx_buffer_tail)
  {
    serial1_rx_buffer[serial1_rx_buffer_head] = data;
    serial1_rx_buffer_head = next_head;
  }
  else
  {
    next_head++;
    next_head--;
  }
}

之后我们就可以编写一个/dev/ttyS1设备文件用于串口1的驱动:

int ttyS1_open(struct file *fs)
{
  return 0;
}


int ttyS1_close(struct file *fs)
{
  return 0;
}


size_t ttyS1_read(struct file *fs, void *buff, size_t size)
{
  uint8_t *p = (uint8_t *)buff;
  size_t read_len = 0;
  for (int i = 0; i < size; i++)
  {
    if (!serial1_read(&p[read_len]))
    {
      return read_len;
    }
    read_len++;
  }
  return size;
}


size_t ttyS1_write(struct file *fs, const void *buff, size_t size)
{
  uint8_t *p = (uint8_t *)buff;
  for (int i = 0; i < size; i++)
  {
    serial1_write(p[i]);
  }
  return size;
}


void ttyS1_init(void)
{
  file_operations_s ops = {0};


  ops.open = ttyS1_open;
  ops.close = ttyS1_close;
  ops.write = ttyS1_write;
  ops.read = ttyS1_read;
  ops.ioctl = NULL;


  fs_register_dev("/dev/ttyS1", ops);
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    18290

    浏览量

    222194
  • 计算机
    +关注

    关注

    19

    文章

    6651

    浏览量

    84562
  • Unix系统
    +关注

    关注

    0

    文章

    15

    浏览量

    9581
  • Cortex-M3
    +关注

    关注

    9

    文章

    268

    浏览量

    59165
  • 串口输出
    +关注

    关注

    0

    文章

    16

    浏览量

    7387
收藏 人收藏

    评论

    相关推荐

    力天手把手教你学单片机视频全集下载

    .rarhttp://115.com/file/e7fv6828#17.第九讲.输出型外设与51的IO口上--力天手把手教你学单片机之实战篇.rarhttp://115.com/file
    发表于 02-14 17:06

    小七免杀论坛vip 2013源码免杀培训课程

    实战篇----瑞星还能再低调么?第十三课:源码免杀实战篇----江民这是肿么了.第十四课:源码免杀实战篇----诺顿(百度说你是世界三大杀毒哇)第十五课:源码免杀实战篇----AVG(
    发表于 10-05 17:35

    【连载贴】【NetRotuer之像学单片机一样学linux笔记】一、目录

    1.8.2写一个页面配置系统 1.8.3页面数据与服务器交互 1.8.4php以及lighttpd安装1.9. 实战篇之串口转wifi2.0. 实战篇之wifi读卡器 2.1.实战篇之点阵广告系统 2.2
    发表于 02-16 17:38

    2年磨成一剑,《Zigbee深入浅出实战篇》原创超高清视频教程部分,不看别后悔

    Zigbee一线在职工程师,2年磨成一剑,《Zigbee深入浅出实战篇》原创超高清视频教程部分,不看别后悔第一讲上 http://www.tudou.com/programs/view
    发表于 05-04 13:45

    【IMX6UL开发板试用体验】- 项目前 - USB 设备驱动实战篇

    函数,当然拔掉设备自然是运行我们的disconnect ,最后根据判断的语句对USB 设备的各个端口进行打印。结果如下:因为这是 实战的前,所以不过多的讲解,而在后面安排了用USB接
    发表于 03-07 23:06

    用Python让单片机“行动”起来——MicroPython实战入门

    了。MicroPython-TPYBoard QQ技术交流群:157816561  MicroPython开发板入门实战篇  MicroPython脱胎于Python,基于ANSIC(C语言标准),然后在语法上又
    发表于 08-21 17:10

    《HELLO+FPGA》-+项目实战篇

    《HELLO+FPGA》-+项目实战篇
    发表于 09-27 10:08

    【电子书】《HELLO FPGA》- 项目实战篇

    `项目实战篇以例举三人表决器、数字时钟、多终端点歌系统、数字示波器这四个实际的工程项目,手把手带领大家从分析工程、分解工程到最终实现工程。`
    发表于 04-06 14:20

    STM32MP1系列教程之Cortex-M4开发

    ,后续会陆续更多更多系列教程,包括Cortex-A7开发、Cortex-M4开发、Linux应用开发、Linux系统移植、Linux驱动开发
    发表于 08-04 06:40

    如何开发符合AUTOSAR规范的电机控制器软件

    基于AUTOSAR规范的电机控制器软件开发本系列文章主要介绍如何开发符合AUTOSAR规范的电机控制器软件的详细过程。全系类分为基础实战篇:基础内简要介绍最新的AUTOSAR规范,嵌入式
    发表于 08-30 08:59

    Linux和RTOS的时钟和定时器怎么使用

    定时器1.7初始化和脱离定时器1.8启动和停止定时器1.9高精度延时1.10实战篇:RTOS定时器代码演示2Linux2.1Linux简介2.2Linux定时器机制2.3alarm类定时器2.4进程
    发表于 01-17 08:13

    触摸按键控制LED学习笔记

    实战篇_流水灯第17节:实战篇_按键控制LED第18节:实战篇_按键控制蜂鸣器(按键消抖)第19节:实战篇_触摸按键控制LED第20节:实战篇
    发表于 02-24 06:24

    笔记本无线上网之实战篇

    笔记本无线上网之实战篇 无线上网实战篇   考虑到CDMA1X方式速率方面以及技术上、功能上比GPRS更先进,
    发表于 01-18 11:14 326次阅读

    项目实战篇

    项目实战篇,VHDL资料,又需要的下来看看
    发表于 08-08 17:03 92次下载

    刘润5分钟商学院之实战篇电子版下载

    刘润5分钟商学院之实战篇电子版下载
    发表于 09-03 16:31 0次下载