0

三天前我安装了 GNAT-GPS 和 AVR-ELF 来玩。我眨了眨眼,以为我可以再玩一些。我没有非 VHDL Ada 经验。

这是我在 C 中工作的场景:

我进行了设置,以便使用 GPIO typedef,我可以参考设置 GPIO 引脚所需的所有信息(即引脚号、引脚注册地址、dd 注册地址和端口注册地址)。然后我对 LED0 做同样的事情,这样逻辑上我可以将 LED0 连接到 GPIO15,它本身连接到 AVR 微控制器的 PB1。

我尝试在 Ada 中做同样的事情。我觉得我可能正在用 Ada 写 C;如果在 Ada 中有更好的方法可以做到这一点,请随时告诉我。

我为特定引脚设置了 AVR 寄存器以连接到其短名称参考:

       -- PB1
   PB1_Port_reg : Unsigned_8;
   PB1_Dd_reg   : Unsigned_8;
   PB1_Pin_reg  : Unsigned_8;
   for PB1_Port_reg'Address use AVR.Atmega328p.PORTB'Address;
   for PB1_Dd_reg'Address use AVR.Atmega328p.DDRB'Address;
   for PB1_Pin_reg'Address use AVR.Atmega328p.PINB'Address;
   PB1_Pin : constant := 1;

然后我设置它的短名称引用以连接到它的封装引脚号:

   -- ATmega328p DIP28 Pin15 is PB1
   Pin15_Port_reg : Unsigned_8;
   Pin15_Dd_reg   : Unsigned_8;
   Pin15_Pin_reg  : Unsigned_8;
   for Pin15_Port_reg'Address use PB1_Port_reg'Address;
   for Pin15_Dd_reg'Address use PB1_Dd_reg'Address;
   for Pin15_Pin_reg'Address use PB1_Pin_reg'Address;
   Pin15_Pin : constant := PB1_Pin;

接下来,我定义一个记录来保存引脚的所有参数:

   type gpio_t is record
      pin   : Unsigned_8;
      pin_reg   : Unsigned_8;
      dd_reg    : Unsigned_8;
      port_reg  : Unsigned_8;
   end record;

这是为了允许我编写以下函数:

 procedure gpio_map (gpio_t_dest : in out gpio_t; gpio_t_pin, gpio_t_pin_reg, gpio_t_dd_reg, gpio_t_port_reg : in Unsigned_8) is

   begin
      gpio_t_dest.pin       := gpio_t_pin;
      gpio_t_dest.pin_reg   := gpio_t_pin_reg;
      gpio_t_dest.dd_reg    := gpio_t_dd_reg;
      gpio_t_dest.port_reg  := gpio_t_port_reg;     
   end gpio_map;

将来,我希望将其设置为:

procedure gpio_map_future (gpio_t_dest : in out gpio_t; gpio_t_src : in gpio_t) is

       begin
          gpio_t_dest.pin       := gpio_t_src.pin;
          gpio_t_dest.pin_reg   := gpio_t_src.pin_reg;
          gpio_t_dest.dd_reg    := gpio_t_src.dd_reg;
          gpio_t_dest.port_reg  := gpio_t_src.port_reg;     
       end gpio_map;

此 gpio_map 函数用于将封装引脚 gpio_t 连接到封装引脚号:

gpio_map(gpio15, Pin15_pin, Pin15_pin_reg, Pin15_dd_reg, Pin15_port_reg);

如果我使用此功能,我发现 LED 已正确初始化:

core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(Pin15_pin))); -- works

但是如果我这样做没有正确初始化:

core_reg_write(gpio15.dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- does not work

但是,这有效:

core_reg_write(Pin15_dd_reg, Shift_Left(1,Integer(gpio15.pin))); -- works

我很清楚我有

Pin15_pin = 1 @ address (don't care - a variable)
Pin15_pin_reg = (don't care) @ address 0x23
Pin15_dd_reg = (0b00000000) @ address 0x24
Pin15_port_reg = (don't care) @ address 0x25

然后

gpio15.pin = 1 @ address (don't care, but not same as Pin15_pin address)
gpio15.pin_reg = (don't care) @ address IS NOT 0x23
gpio15.dd_reg = (don't care) @ address IS NOT 0x24
gpio15.port_reg = (don't care) @ address IS NOT 0x25

如何维护记录成员的固定内存地址,即获取

gpio15.pin_reg = (don't care) @ address 0x23
gpio15.dd_reg = (don't care) @ address 0x24
gpio15.port_reg = (don't care) @ address 0x25

如果我也能得到,那就更好了

gpio15.pin = 1 @ address (same as Pin15_pin address)

很抱歉这个问题很长;希望它有助于说清楚。

4

4 回答 4

1

通过分配这两种类型,您无法真正得到想要的东西。所做的只是复制当前值,而不是寄存器地址。这是一个选项:

创建一个与您的 gpio_t 类型相似的类型,但使其与您的微控制器的寄存器映射完全匹配。这意味着您不会在其中存储引脚号,您需要包括所有周围的寄存器。这是我从另一个文件中找到的用于不同微的示例,但希望可以作为示例

type Register_Layout is limited record
      DIR      : Unsigned_32;
      DIRCLR   : Unsigned_32;
      DIRSET   : Unsigned_32;
      DIRTGL   : Unsigned_32;
      OUTVAL   : Unsigned_32;
      OUTCLR   : Unsigned_32;
      OUTSET   : Unsigned_32;
      OUTTGL   : Unsigned_32;
      INPUT    : Unsigned_32;
      CTRL     : Unsigned_32;
      WRCONFIG : Unsigned_32;
      EVCTRL   : Unsigned_32;
   end record
      with
         Pack,
         Volatile,
         Size => 12*32;

记录类型应该受到限制,以确保它是通过引用而不是通过副本传递的。

注意:您也可以使用表示子句来提供结构的字节和位布局。这取决于您使用的编译器。

一旦你的微寄存器与数据表相匹配,你就可以创建一个变量并将其映射到你想要的地址,就像你对单个变量所做的那样

Register_B : Register_Layout with
      Address => System'To_Address(Some_Address),
      Volatile => True,
      Import => True;

这会将整个记录变量映射到该地址。

之后,您需要修改函数调用以将整个记录作为参数,而不仅仅是寄存器。举个例子:

Core_Reg_Write_DIR(Register_B, Shift_Left(1,Integer(PB1_Pin)));

如果您需要让事情变得更花哨并通过引脚选择正确的寄存器和掩码值,那么您需要使用

  1. 案例陈述
  2. 访问类型/地址的数组(使用引脚类型作为索引)。
  3. 一种从引脚计算寄存器地址和掩码的方法,并将其用于使用引脚作为参数的函数调用内的本地声明变量的地址属性。

您不能真正以不同的方式处理单个记录组件(在 C 和 C++ 中也是如此)。

于 2018-12-02T14:54:53.893 回答
0

经过一番思考,我决定继续我已经在 C 中所做的工作。在那里,我定义了以下 typedef

typedef struct {
    IO_REG_TypeDef_t portr;
    IO_REG_TypeDef_t ddr;
    IO_REG_TypeDef_t pinr;
    volatile uint8_t pin;
} GPIO_TypeDef_t;

并且 IO_REG_t 本身定义为

typedef struct {
    volatile uint8_t* io_reg;
} IO_REG_TypeDef_t;

很明显,gpio 的关键参数被放在 typedef 中。我想在 Ada 做同样的事情。再次,如果我在 Ada 中说 C,请原谅我;随意提出更多的 Ada 标准方法。

我定义了 gpio 引脚组件:

   -- GPIO15 is PB1 on ATmega328p 28 DIP
   gpio15_pin_reg : Unsigned_8;
   for gpio15_pin_reg'Address use Atmega328p.PINB'Address;
   gpio15_dd_reg : Unsigned_8;
   for gpio15_dd_reg'Address use Atmega328p.DDRB'Address;
   gpio15_port_reg : Unsigned_8;
   for gpio15_port_reg'Address use Atmega328p.PORTB'Address;
   gpio15_pin : constant Unsigned_8 := 1;

寄存器读写函数定义:

   procedure core_reg_write (reg: in out Unsigned_8; value: in Unsigned_8) is
   begin
      reg := value;
   end core_reg_write;

   function core_reg_read (reg: in Unsigned_8) return Unsigned_8 is
      value : Unsigned_8;
   begin
      value := reg;
      return value;
   end core_reg_read;

然后定义一个记录,这一次,围绕 pin 变量,而不是 pin、dd 和端口寄存器的变量,而是它们的地址:

   type gpio_t is record
      pin       : Unsigned_8;
      pin_reg_addr  : System.Address;
      dd_reg_addr   : System.Address;
      port_reg_addr     : System.Address;
   end record;

组装给定 gpio 引脚的记录:

   gpio15 : gpio_t := (gpio15_pin, gpio15_pin_reg'Address, gpio15_dd_reg'Address, gpio15_port_reg'Address);

定义了获取此记录并设置引脚参数的过程:

   procedure gpio_output (gpio : in gpio_t) is
      dd_reg : Unsigned_8;
      for dd_reg'Address use gpio.dd_reg_addr;
   begin
      core_reg_write(dd_reg, core_reg_read(dd_reg) or shift_left(1,integer(gpio.pin)));
   end gpio_output;

   procedure gpio_hi (gpio : in gpio_t) is
      port_reg : Unsigned_8;
      for port_reg'Address use gpio.port_reg_addr;
   begin
      core_reg_write(port_reg, core_reg_read(port_reg) or shift_left(1,integer(gpio.pin)));
   end gpio_hi;

   procedure gpio_lo (gpio : in gpio_t) is
      port_reg : Unsigned_8;
      for port_reg'Address use gpio.port_reg_addr;
   begin
      core_reg_write(port_reg, core_reg_read(port_reg) and not shift_left(1,integer(gpio.pin)));
   end gpio_lo;

在这些过程的每一个中,由于缺乏更好的描述,需要的寄存器被手动取消引用。

以下序列遵循 begin 关键字:

 -- Initialize
   gpio_output(gpio15); 

 For_loop_0:
   loop

    -- turn on
        gpio_hi(gpio15);

        -- loop
      Lazy_delay_1:
    for I in Unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_1;

    -- turn off
        gpio_lo(gpio15);

        -- loop
      Lazy_delay_2:
    for I in Unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_2;

   end loop For_loop_0;

LED 闪烁。

这实现了我想要的,但我对采用复合 gpio_t 类类型并且不需要手动取消引用地址/指针的其他方法持开放态度。

于 2018-12-07T05:33:15.150 回答
0

好的,在查看了您的示例后,我在 Ada 中提出了类似的解决方案。也就是说,我并不真正关心这里的访问类型是如何暴露的。我会留下我之前的答案,因为我觉得直接使用记录是一种更好的方法,但是为了具体回答你的问题,这里是我在 GNAT GPL 2017 中使用手工运行时测试的一个例子(对于另一个芯片,但它已经足够了验证编译)。尝试在 GNAT 的非嵌入式版本中编译它会遇到编译器崩溃(我假设是因为地址对 Windows 不利)。希望这提供了一个更符合您个人要求的示例

寄存器.ads

with Interfaces;

-- Basic Register type and functionality
package Registers with Pure is

   type Register is limited private;
   type Register_Access is access all Register with Storage_Size => 0;

   procedure Core_Reg_Write
      (Target : not null Register_Access;
       Value  : Interfaces.Unsigned_8)
      with Inline;

   function  Core_Reg_Read
      (Source : not null Register_Access) 
       return Interfaces.Unsigned_8
      with Inline;

private

   type Register is limited record
      Value : Interfaces.Unsigned_8;
   end record
      with Volatile, Size => 8;

end Registers;

寄存器.adb

package body Registers is

   procedure Core_Reg_Write
      (Target : not null Register_Access;
       Value  : Interfaces.Unsigned_8)
   is begin

      Target.Value := Value;

   end Core_Reg_Write;

   function  Core_Reg_Read
      (Source : not null Register_Access) 
       return Interfaces.Unsigned_8
   is begin

      return Source.Value;

   end Core_Reg_Read;

end Registers;

io_registers.ads

with Registers;

-- Specific Register types and functionality
package IO_Registers with Pure is

   -- Use different ones for each register to avoid accidental copy/paste
   -- errors.
   type Port_Register is new Registers.Register_Access;
   type DD_Register   is new Registers.Register_Access;
   type Pin_Register  is new Registers.Register_Access; 

   type Pin_Number is new Positive range 1 .. 8;      

   type GPIO_Register is record
      Port_Reg : Port_Register;
      DD_Reg   : DD_Register;
      Pin_Reg  : Pin_Register;
      Pin      : Pin_Number;
   end record;

end IO_Registers;

预定义的_registers.ads

with Registers;
with System;

package Predefined_Registers is

   -- Fake addresses here, since I don't have your atmega package
   GPIO_15_Pin_Reg : aliased Registers.Register
      with 
         Address => System'To_Address(16#80000400#),
      Volatile,
      Convention => C,
      Import;

   GPIO_15_DD_Reg : aliased Registers.Register 
      with 
         Address => System'To_Address(16#80000401#),
      Volatile,
      Convention => C,
      Import;       

   GPIO_15_Port_Reg : aliased Registers.Register
      with 
         Address => System'To_Address(16#80000402#),
      Volatile,
      Convention => C,
      Import;

   GPIO_15_Pin : constant := 1;

end Predefined_Registers;

程序.adb

with IO_Registers;
with Predefined_Registers;

procedure Program is
   GPIO_15 : IO_Registers.GPIO_Register :=
               (Port_Reg => Predefined_Registers.GPIO_15_Port_Reg'Access,
                Pin_Reg  => Predefined_Registers.GPIO_15_Pin_Reg'Access,
                DD_Reg   => Predefined_Registers.GPIO_15_DD_Reg'Access,
                Pin      => Predefined_Registers.GPIO_15_Pin);
begin
   -- Notice the use of IO_Registers for this call.  The new types were
   -- created there, so the corresponding ops were too
   IO_Registers.Core_Reg_Write(GPIO_15.Port_Reg,16#01#);
end Program;
于 2018-12-07T23:19:06.877 回答
0

在玩了一会儿之后,在这个在线编译器(https://www.tutorialspoint.com/compile_ada_online.php)中,我得到了这个工作:


with Ada.Text_IO; use Ada.Text_IO;
with Interfaces; use Interfaces;
with System; use System;

procedure Hello is

    -- pseudo hardware registers, unknown addresses, known contents
    temp0 : interfaces.unsigned_8 := 2#00000101#; -- pinr
    temp1 : interfaces.unsigned_8 := 2#10000000#; -- ddr
    temp2 : interfaces.unsigned_8 := 2#10000000#; -- portr

    -- core
    type io_reg_t is limited record
        io_reg  :   interfaces.unsigned_8;
    end record;
    pragma volatile(io_reg_t); --  Verify relevance.

    -- processor
    gpio15_pinr : aliased io_reg_t;
    for gpio15_pinr'address use temp0'address;
    gpio15_ddr : aliased io_reg_t;
    for gpio15_ddr'address use temp1'address;    
    gpio15_portr : aliased io_reg_t;
    for gpio15_portr'address use temp2'address;
    gpio15_pin : constant interfaces.unsigned_8 := 1;

    procedure core_reg_write_old (reg: in out unsigned_8; value: in unsigned_8) is
    begin
      reg := value;
    end core_reg_write_old;

    procedure core_reg_write (reg: access io_reg_t; value: in unsigned_8) is
    begin
      reg.io_reg := value;
    end core_reg_write;

    function core_reg_read (reg: access io_reg_t) return Unsigned_8 is
    begin
      return reg.io_reg;
    end core_reg_read;    

    -- gpio
    type gpio_t is record
        pinr    :   access io_reg_t;
        ddr     :   access io_reg_t;
        portr   :   access io_reg_t;
        pin     :   interfaces.unsigned_8;
    end record;
    pragma volatile(gpio_t); -- Verify relevance.

    procedure gpio_output (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
    end gpio_output;

    procedure gpio_hi (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
    end gpio_hi;

    procedure gpio_lo (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
    end gpio_lo; 

    gpio15 : gpio_t := (
        pinr    => gpio15_pinr'access,
        ddr     => gpio15_ddr'access,
        portr   => gpio15_portr'access,
        pin     => gpio15_pin
    );

    -- led
    type led_t is record
        gpio    :   gpio_t;
    end record;

    led0 : led_t := (gpio => gpio15);

    procedure led_init (led : in led_t) is
    begin
        gpio_output(led.gpio);
    end led_init;

    procedure led_on (led : in led_t) is
    begin
        gpio_hi(led.gpio);
    end led_on;

    procedure led_off (led : in led_t) is
    begin
        gpio_lo(led.gpio);
    end led_off;

begin
  put_line("Hello, world!");
  -- Does it match the original value of 5?
  put_line(gpio15.pinr.io_reg'Image);

  -- Does modification via variable alter the value returned?
  temp0 := 203;
  put_line(gpio15.pinr.io_reg'Image);

  -- Does modification via record alter the value returned?
  gpio15.pinr.io_reg := 89;
  put_line(gpio15.pinr.io_reg'Image);

  -- Writes value in temp2 (128) to temp0.
  core_reg_write_old(temp0,temp2);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  -- Writes value of pin (1) to pinr via record.  
  --core_reg_write(gpio15.ddr,gpio15.pin);

  -- Writes 1 shifted value of pin times and or's that with ddr reg
  --gpio_output(gpio15);
  led_init(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  --gpio_hi(led0.gpio);  
  led_on(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

  --gpio_lo(led0.gpio);   
  led_off(led0);

  put_line(gpio15.pinr.io_reg'Image);
  put_line(gpio15.ddr.io_reg'Image);
  put_line(gpio15.portr.io_reg'Image);
  put_line(gpio15.pin'Image);

end Hello;

我为我的嵌入式环境修改了它,但它无法编译,并抱怨:

undefined reference to `__gnat_last_chance_handler’

对于“reg.io_reg := value”和“return reg.io_reg”行。

我发现如果我的访问类型被显式声明为“not null”,我实际上并不需要 last_chance_handler。

所以更新后的程序变成了:


with Interfaces; use Interfaces;
with System;
with Atmega328p;

procedure Main is

   -- core
    type io_reg_t is limited record
        io_reg  :   interfaces.unsigned_8;
    end record;
    pragma volatile(io_reg_t); -- Verify relevance.

    type dd_io_reg_t is new io_reg_t;

    -- Location?
    gpio15_pinr : aliased io_reg_t;
    for gpio15_pinr'address use Atmega328p.PINB'Address;
    gpio15_ddr : aliased io_reg_t;
    for gpio15_ddr'address use Atmega328p.DDRB'Address;    
    gpio15_portr : aliased io_reg_t;
    for gpio15_portr'address use Atmega328p.PORTB'Address;
    gpio15_pin : constant interfaces.unsigned_8 := 1; 

    procedure core_reg_write (reg: not null access io_reg_t; value: in interfaces.unsigned_8) is
    begin
        reg.io_reg := value;
    end core_reg_write;

    function core_reg_read (reg: not null access io_reg_t) return interfaces.unsigned_8 is
    begin
        return reg.io_reg;
    end core_reg_read;    

    -- gpio
    type gpio_t is record
        pinr    :   not null access io_reg_t;
        ddr     :   not null access io_reg_t;
        portr   :   not null access io_reg_t;
        pin     :   interfaces.unsigned_8;
    end record;
    pragma volatile(gpio_t); --  Verify relevance.

    -- gpio_output
    procedure gpio_output (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.ddr,core_reg_read(gpio.ddr) or shift_left(1,integer(gpio.pin)));
    end gpio_output;

    procedure gpio_hi (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) or shift_left(1,integer(gpio.pin)));
    end gpio_hi;

    procedure gpio_lo (gpio : in gpio_t) is
    begin
        core_reg_write(gpio.portr,core_reg_read(gpio.portr) and not shift_left(1,integer(gpio.pin)));
    end gpio_lo; 

    gpio15 : gpio_t := (
        pinr    => gpio15_pinr'access,
        ddr     => gpio15_ddr'access,
        portr   => gpio15_portr'access,
        pin     => gpio15_pin
    );

    -- led
    type led_t is record
        gpio    :   gpio_t;
    end record;

    led0 : led_t := (gpio => gpio15);

    procedure led_init (led : in led_t) is
    begin
        gpio_output(led.gpio);
    end led_init;

    procedure led_on (led : in led_t) is
    begin
        gpio_hi(led.gpio);
    end led_on;

    procedure led_off (led : in led_t) is
    begin
        gpio_lo(led.gpio);
    end led_off;

begin

    -- Initialize
    -- Writes value of pin (1) to pinr via record.  
    --core_reg_write(gpio15.ddr,gpio15.pin);

    -- Writes 1 shifted value of pin times and or's that with ddr reg
    --gpio_output(gpio15);
    led_init(led0);

 For_loop_0:
   loop

        -- turn on
        --gpio_hi(led0.gpio);  
    led_on(led0);

        -- loop
      Lazy_delay_1:
    for i in interfaces.unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_1;

        -- turn off
        --gpio_lo(led0.gpio);   
    led_off(led0);

        -- loop
      Lazy_delay_2:
    for i in interfaces.unsigned_32 range 0 .. 100_000 loop
        null;
    end loop Lazy_delay_2;

   end loop For_loop_0;

end Main;

在此修改后,我将其编译到微控制器中。

LED 闪烁。

我会用这个前进。

于 2018-12-14T02:28:31.313 回答