element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog Arty S7 50 ArtyBot Pulse Width Modulation (PWM) for Motor Speed Control
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
FPGA requires membership for participation - click to join
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: javagoza
  • Date Created: 20 Jun 2022 7:25 PM Date Created
  • Views 9174 views
  • Likes 12 likes
  • Comments 2 comments
  • axi timer
  • pwm
  • xilinx
  • fpga
  • vivado
  • digilent
  • fpga-project
  • arty s7
  • spartan-7
  • analog discovery 2
  • vitis
  • Spartan_Migration
  • microblaze
  • IP Block
  • spartan-6
Related
Recommended

Arty S7 50 ArtyBot Pulse Width Modulation (PWM) for Motor Speed Control

javagoza
javagoza
20 Jun 2022
Arty S7 50 ArtyBot Pulse Width Modulation (PWM) for Motor Speed Control

In the previous blogs in this series for the "7 Ways to Leave Your Spartan-6" program we learned how to use the AMD-Xilinx Vivado and the AMD-Xilinx Vitis. We used Vivado for creating hardware designs for the Arty S7 board with a Hardware Design Language like Verilog and also we made an introduction to Block Design and the Microblaze soft-processor  using pre-built IP-Blocks. Then we used Vitis IDE and the Xilinx SDK for creating software in C/C++ that runs in the FPGA.

In this blog we are going to introduce the ArtyBot, a robotic system based in the TI-RSLK robotic system kit using the Xilinx Spartan-7 FPGA of the Digilent Arty S7 50 board.

"7 Ways to Leave Your Spartan-6" related posts
1 Arty S7 50 First Power Up and Hardware-Only Blinky
2 Arty S7 50 First Baremetal Software Project
3 Arty S7 50 Rapid Prototyping - Environmental Monitor
4 Arty S7 50 ArtyBot Pulse Width Modulation (PWM) Motor Speed Control
5 Arty S7 50 ArtyBot Custom AXI4 Lite IP Peripheral for Sensing Motor Rotational Speed
6 Arty S7 50 ArtyBot How to Store MicroBlaze Program in the Quad-SPI Flash from Vivado
7 Arty S7 50 ArtyBot - Bot Application Framework
8 Arty S7 50 ArtyBot becomes Emubot, an educational robot for young children
9 Arty S7 50 ArtyBot - Color sensing and line follower
10 Arty S7 50 The Spartan-6 Migration Path Game. Learning the Differences Between Spartan-6 and Spartan-7 FPGAs
11 Arty S7 50 ArtyBot ToF Sensor for Obstacle Avoidance. Vivado Hierarchical Blocks
12 "7 Ways to Leave Your Spartan-6 FPGA" program. My summary
13 Arty S7 50 VGA Thermal Imaging Camera

We'll describe how to configure a Motor Driver and Power Distribution Board to use the same logic levels as the Arduino/chipKIT Shield Connector ports on the Arty S7 board.  Then we will make an introduction to Pulse Width Modulation (PWM) method to control the speed in DC motors. We'll use the AXI Timer/Counter Intellectual Property (IP) to generate PWM signals.

And we will use a Xilinx® Spartan®-6 FPGA based multi-function instrument, the Digilent Analog Discovery 2 for debugging our designs.

Video showing the final solution of this tutorial:

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image


Tutorial Environment

For this tutorial you'll need, Windows 10, AMD Xilinx Vivado ML 2021.1 and the following hardware:

Product Name Manufacturer Datasheet
Arty S7 50  Digilent  Buy Now
TI-RSLK element14 Buy Now

Also we'll use a Logic Analyzer, the Digilent Analog Discovery 2 USB Oscilloscope


ArtyBot Xilinx Spartan-7 FPGA Robotic System

Using the TI-RSLK Maze Edition Curriculum and its accompanying hardware kit as inspiration, we are going to design a robotics system that uses the Arty S7 board with its Xilinx Spartan-7.

Reference: Classic TI-RSLK Maze Edition Curriculum

In this tutorial we will use the Romi chassis with two motors and the Pololu Motor driver and power distribution board.


Romi chassis

The Romi chassis is a differential-drive mobile robot platform with a diameter of 6.5″ (165 mm) and an integrated battery holder for six AA batteries. The drive wheels are located on a diameter of the circular base plate, allowing for turns or spinning in place. A large, fixed ball caster in the rear provides a smooth third point of contact.

image

Reference: Pololu - Romi Chassis Kit - Red


Motor driver and power distribution board

This motor driver and power distribution board is designed specifically for the Romi chassis as a convenient way to drive the chassis' motors and power the rest of the electronics that make up your robot. It features two DRV8838 motor drivers, one for each of the chassis' motors, and a powerful switching step-down regulator that can supply a continuous 2.5 A at 5 V or 3.3 V.

image

The output driver block consists of Nchannel power MOSFETs configured as an H-bridge to drive the motor winding. An internal charge pump generates needed gate drive voltages.

Reference: Pololu - Motor Driver and Power Distribution Board for Romi Chassis


DRV8838 Low-Voltage H-Bridge Driver

The DRV8838 is an H-bridge driver that can drive one dc motor or other devices like solenoids. The outputs are controlled using a PH-EN interface. It includes a low-power sleep mode, which can be enabled using the nSLEEP pin. It integrates the necessary driver FETs and FET control circuitry into a single device. In addition, it adds protection features beyond traditional discrete implementations: undervoltage lockout, overcurrent protection, and thermal shutdown.

image

Reference: DRV8838 data sheet, product information and support | TI.com


H-Bridge Control

The DRV8838 is controlled using a PHASE/ENABLE interface. This interface uses one pin to control the H-Bridge current direction, and one pin to enable or disable the H-Bridge.

Logic table for the DRV8838

nSLEEP PH EN OUT1 OUT2 FUNCTION (DC Motor)
0 X X Z Z Coast
1 X 0 L L Brake
1 1 1 L H Reverse
1 0 1 H L Forward

Reference:

DRV883x Low-Voltage H-Bridge Driver datasheet (Rev. F) (ti.com)


Motor Driver and Power Distribution Board Modifications

Configure the Motor Driver and Power Distribution Board (MDPDB), We need to set the logic levels of the Motor Driver board to the logic levels used in the GPIOs of the Arty S7 board.

Follow these steps to configure the motor driver board for the ArtyBot:

  1. Cut the VPU—VREG jumper on the MDPDB
  2. Cut the VCCMD—VREG jumper on the MDPDB
  3. Connect a ground wire from the MDPDB to ground on the Arty S7
  4. Connect VREG (+5V) from the MDPDB to +5V VCC5V0 on the Arty S7
  5. Connect VPU from the MDPDB to 3.3V VCC on the Arty S7. Use VCC form a PMOD header
  6. Connect VCCMD from the MDPDB to 3.3V VCC on the Arty S7. Use VCC form a PMOD header

We will power the Arty S7 board through the VCC5V0 pin (JP13 power select) 

Connect VREG (+5V) from the MDPDB to +5V VCC5V0 on the Arty S7

image

Powering the Arty S7 with the Power Distribution Board 

Powering the Arty S7 with the Motor Driver and Power Distribution Board (MDPDB)

For the reference logic levels we can use the two VCC3V3 (3.3V) outputs from an unused PMOD connector.

image

image

Reference: arty_s7_sch-rev_b.pdf(digilent.com)


Arduino/chipKIT Shield Connector

We will design the PWM, DIR, nSLEEP outputs connected to the standard Arduino and chipKIT shield of the Arty S7. The shield connector has 45 pins connected to the FPGA for general purpose Digital I/O. It is possible to use these pins for just about anything including digital read/write, SPI connections, UART connections, I2C connections, and PWM.

Six of these pins (labeled AN0-AN5) can also be used as single-ended analog inputs with an input range of 0V-3.3V, and another four (labeled AN6-9) can be used as differential analog input pairs with an input range of 0V-1.0V.

The Arty S7's shield connector is implemented on headers J1, J2, J3, J5, J6, and J8.

image

MDPDB Arty S7
PWM Right IO0
DIR  Right IO1
nSLEEP Right IO2
PWM Left IO3
DIR Left IO4
nSLEEP Left IO5

Constrains

## ChipKit Outer Digital Header
# Right Motor Control
set_property -dict { PACKAGE_PIN L13   IOSTANDARD LVCMOS33 } [get_ports { pwm0_0 }]; #IO_0_14 Sch=ck_io[0]
set_property -dict { PACKAGE_PIN N13   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[0] }]; #IO_L6N_T0_D08_VREF_14   Sch=ck_io[1]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=ck_io[2]
# Left motor Control
set_property -dict { PACKAGE_PIN R14   IOSTANDARD LVCMOS33 } [get_ports { pwm0_1 }]; #IO_L13P_T2_MRCC_14      Sch=ck_io[3]
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[2] }]; #IO_L13N_T2_MRCC_14      Sch=ck_io[4]
set_property -dict { PACKAGE_PIN R16   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[3] }]; #IO_L14P_T2_SRCC_14      Sch=ck_io[5]

Reference: Arty S7 Reference Manual - Digilent Reference


Pulse Width Modulation

We will use PWM to control the speed of the two motors. Pulse Width Modulation (PWM) control or Pulse Duration Modulation (PDM) is a very ingenious method used to control the average power delivered by an electrical signal to a load, by effectively chopping it up into discrete parts. It is a method that is very easy to implement and has high efficiency. The PWM signal is essentially a high frequency (typically greater than 1KHz) square wave. The duty cycle of this square wave is varied to vary the power supplied to the load. The main uses of PWM, but not limited to these, are power delivery control, voltage regulation, audio effects and amplification, dimming lights, modulation of signals in telecommunications, control of motor rotation angle in servomotors.

image

Motor speed control with PWM

The electrical power (P) delivered to the motor is the product of voltage (V), current (C) and duty cycle.

P = V * I * duty

Depending of the polarity of the current and voltage the motors can spin forward or backward.

The motor converts electrical power into mechanical power that delivers torque causing the wheel to spin. Torque is force * distance. 

We will fix the voltage and the current will depend on the friction caused by the mechanical load.

Reference: Pulse-width modulation - Wikipedia


AXI Timer/Counter Intellectual Property (IP)

There are many ways to generate a PWM signal with FPGA. We will use the simplest way a preconfigured IP block. The AXI Timer/Counter is a 32-bit timer module that attaches to the AXI4-Lite interface. In Cascade mode, it can be used as 64-bit timer module.

This Xilinx® LogiCORETm IP module is provided at no additional cost with the Xilinx Vivado® Design Suite.

AXI interface is based on the AXI4-Lite specification

  • Two programmable interval timers with interrupt, event generation, and event capture capabilities
  • Configurable counter width
  • One Pulse Width Modulation (PWM) output
  • Freeze input for halting counters during software debug

The Xilinx timer/counter supports the following features:

  • Polled mode.
  • Interrupt driven mode
  • enabling and disabling specific timers
  • PWM operation
  • Cascade Operation (This is to be used for getting a 64 bit timer and this feature is present in the latest versions of the axi_timer IP)

Block Diagram of the AXI Timer/Counter

image

We are interested in Pulse Width Modulation (PWM) Mode. In PWM mode, two timer/counters are used as a pair to produce an output signal (PWM0) with a specified frequency and duty factor.

  • Timer0 sets the period
  • and Timer1 sets the high time for the PWM0 output.

PWM Mode has the following characteristics:

  • The mode for both Timer0 and Timer1 must be set to Generate Mode (bit MDT in the TCSR set to ’0’).
  • The PWMA0 bit in TCSR0 and PWMB0 bit in TCSR1 must be set to ’1’ to enable PWM mode.
  • The Generate Out signals must be enabled in the TCSR (bit GENT set to ’1’). The PWM0 signal is generated from the Generate Out signals of Timer0 and Timer1, so these signals must be enabled in both timer/counters.
  • The assertion level of the Generate Out signals for both timers in the pair must be set to ’1’. This is done by setting C_GEN0_ASSERT and C_GEN1_ASSERT to ’1’.
  • The counter can be set to count up or down.

The PWM period is determined by the generate value in the Timer0 load register (TLR0). The PWM high time is determined by the generate value in the Timer1 load register (TLR1).

The period and duty factor are calculated as follows:
When counters are configured to count up (UDT = ’0’):

  • PWM_PERIOD = (MAX_COUNT - TLR0 + 2) x AXI_CLOCK_PERIOD
  • PWM_HIGH_TIME = (MAX_COUNT - TLR1 + 2) x AXI_CLOCK_PERIOD

When counters are configured to count down (UDT = ’1’):

  • PWM_PERIOD = (TLR0 + 2) x AXI_CLOCK_PERIOD
  • PWM_HIGH_TIME = (TLR1 + 2) x AXI_CLOCK_PERIOD

where MAX_COUNT is the maximum count value for the counter, such as 0xFFFFFFFF for a 32-bit counter.

Reference: AXI Timer/Counter (xilinx.com)


FPGA Hardware design

For this design we will use the following blocks in this order:

  1. Boards DDR3 SDRAM - Autoconnect, this wil create a Mig_7series_0  block
  2. Delete clk_ref_i pin from Mig_7series_0 block
  3. Connect the “ui_addn_clk_0” pin to the “clk_ref_i” of the Mig_7series_0  block
  4. Run connection automation
  5. Add constant block width 12 value 0
  6. Connect dout[11:0] to mig_series_0 device_temp_1[11:0]
  7. Add Clocking Wizard IP
  8. Connect  Clocking Wizard's clk_in1 to the MIG's ui_clk
  9. Connect reset port to MIG's ui_clk_sync_rst
  10. Add Microblaze IP
  11. Run Block Automation: Local Memory 48 KB, Cache Configuration Enabled, Debug Only, Peripheral AXI Port enables, Interrupt Controller enabled. Clock connection MIG's ui_clk
  12. Run connection Automation. Select mig_7series_0 and S-AXI
  13. Drop USB UART from boards: + AXI Uartlite/UART
  14. Run Connection Automation
  15. From Board/GPIO 2RGB LEDS / Connect Board Component...
  16. From Board/GPIO 4 LEDS / Connect Board Component...
  17. Run Connection Automation
  18. From Board/GPIO 4 Push Buttons/ Connect Board Component...
  19. From Board/GPIO 4 Switches/ Connect Board Component...
  20. Run Connection Automation
  21. Add AXI Timer IP.
  22. Make the PWM pin on the AXI Timer external and name it with the same port you define in Master XDC file, pwm0_0
  23. Add AXI Timer IP.
  24. Make the PWM pin on the AXI Timer external and name it with the same port you define in Master XDC file, pwm1_0
  25. Resize  and Connect interrupts to Concat Block
  26. Add AXI GPIO IP width 4 for hbridge control (for DIR_R, nSLEEP_R, DIR_L, nSLEEP_L)  hbridge_control_tri_o[3:0] 
  27. Validate Block Design

image

Add Master XDC file and configure the two external ports defined for pwm and for digital outputs to control h-bridge Direction and Sleep state

## ChipKit Outer Digital Header
# Right Motor Control
set_property -dict { PACKAGE_PIN L13 IOSTANDARD LVCMOS33 } [get_ports { pwm0_0 }]; #IO_0_14 Sch=ck_io[0]
set_property -dict { PACKAGE_PIN N13 IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[0] }]; #IO_L6N_T0_D08_VREF_14 Sch=ck_io[1]
set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=ck_io[2]
# Left motor Control
set_property -dict { PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports { pwm0_1 }]; #IO_L13P_T2_MRCC_14 Sch=ck_io[3]
set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[2] }]; #IO_L13N_T2_MRCC_14 Sch=ck_io[4]
set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[3] }]; #IO_L14P_T2_SRCC_14 Sch=ck_io[5]

Reference: digilent-xdc/Arty-S7-50-Master.xdc at master · Digilent/digilent-xdc (github.com)

This is the file with all the modifications:

## This file is a general .xdc for the Arty S7-50 Rev. E
## To use it in a project:
## - uncomment the lines corresponding to used pins
## - rename the used ports (in each line, after get_ports) according to the top level signal names in the project

## Clock Signals
#set_property -dict { PACKAGE_PIN F14   IOSTANDARD LVCMOS33 } [get_ports { CLK12MHZ }]; #IO_L13P_T2_MRCC_15 Sch=uclk
#create_clock -add -name sys_clk_pin -period 83.333 -waveform {0 41.667} [get_ports { CLK12MHZ }];
#set_property -dict { PACKAGE_PIN R2    IOSTANDARD SSTL135 } [get_ports { CLK100MHZ }]; #IO_L12P_T1_MRCC_34 Sch=ddr3_clk[200]
#create_clock -add -name sys_clk_pin -period 10.000 -waveform {0 5.000}  [get_ports { CLK100MHZ }];

## Switches
#set_property -dict { PACKAGE_PIN H14   IOSTANDARD LVCMOS33 } [get_ports { sw[0] }]; #IO_L20N_T3_A19_15 Sch=sw[0]
#set_property -dict { PACKAGE_PIN H18   IOSTANDARD LVCMOS33 } [get_ports { sw[1] }]; #IO_L21P_T3_DQS_15 Sch=sw[1]
#set_property -dict { PACKAGE_PIN G18   IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L21N_T3_DQS_A18_15 Sch=sw[2]
#set_property -dict { PACKAGE_PIN M5    IOSTANDARD SSTL135 } [get_ports { sw[3] }]; #IO_L6N_T0_VREF_34 Sch=sw[3]

## RGB LEDs
#set_property -dict { PACKAGE_PIN J15   IOSTANDARD LVCMOS33 } [get_ports { led0_r }]; #IO_L23N_T3_FWE_B_15 Sch=led0_r
#set_property -dict { PACKAGE_PIN G17   IOSTANDARD LVCMOS33 } [get_ports { led0_g }]; #IO_L14N_T2_SRCC_15 Sch=led0_g
#set_property -dict { PACKAGE_PIN F15   IOSTANDARD LVCMOS33 } [get_ports { led0_b }]; #IO_L13N_T2_MRCC_15 Sch=led0_b
#set_property -dict { PACKAGE_PIN E15   IOSTANDARD LVCMOS33 } [get_ports { led1_r }]; #IO_L15N_T2_DQS_ADV_B_15 Sch=led1_r
#set_property -dict { PACKAGE_PIN F18   IOSTANDARD LVCMOS33 } [get_ports { led1_g }]; #IO_L16P_T2_A28_15 Sch=led1_g
#set_property -dict { PACKAGE_PIN E14   IOSTANDARD LVCMOS33 } [get_ports { led1_b }]; #IO_L15P_T2_DQS_15 Sch=led1_b

## LEDs
#set_property -dict { PACKAGE_PIN E18   IOSTANDARD LVCMOS33 } [get_ports { led[0] }]; #IO_L16N_T2_A27_15 Sch=led[2]
#set_property -dict { PACKAGE_PIN F13   IOSTANDARD LVCMOS33 } [get_ports { led[1] }]; #IO_L17P_T2_A26_15 Sch=led[3]
#set_property -dict { PACKAGE_PIN E13   IOSTANDARD LVCMOS33 } [get_ports { led[2] }]; #IO_L17N_T2_A25_15 Sch=led[4]
#set_property -dict { PACKAGE_PIN H15   IOSTANDARD LVCMOS33 } [get_ports { led[3] }]; #IO_L18P_T2_A24_15 Sch=led[5]

## Buttons
#set_property -dict { PACKAGE_PIN G15   IOSTANDARD LVCMOS33 } [get_ports { btn[0] }]; #IO_L18N_T2_A23_15 Sch=btn[0]
#set_property -dict { PACKAGE_PIN K16   IOSTANDARD LVCMOS33 } [get_ports { btn[1] }]; #IO_L19P_T3_A22_15 Sch=btn[1]
#set_property -dict { PACKAGE_PIN J16   IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L19N_T3_A21_VREF_15 Sch=btn[2]
#set_property -dict { PACKAGE_PIN H13   IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L20P_T3_A20_15 Sch=btn[3]

## Pmod Header JA
#set_property -dict { PACKAGE_PIN L17   IOSTANDARD LVCMOS33 } [get_ports { ja[0] }]; #IO_L4P_T0_D04_14 Sch=ja_p[1]
#set_property -dict { PACKAGE_PIN L18   IOSTANDARD LVCMOS33 } [get_ports { ja[1] }]; #IO_L4N_T0_D05_14 Sch=ja_n[1]
#set_property -dict { PACKAGE_PIN M14   IOSTANDARD LVCMOS33 } [get_ports { ja[2] }]; #IO_L5P_T0_D06_14 Sch=ja_p[2]
#set_property -dict { PACKAGE_PIN N14   IOSTANDARD LVCMOS33 } [get_ports { ja[3] }]; #IO_L5N_T0_D07_14 Sch=ja_n[2]
#set_property -dict { PACKAGE_PIN M16   IOSTANDARD LVCMOS33 } [get_ports { ja[4] }]; #IO_L7P_T1_D09_14 Sch=ja_p[3]
#set_property -dict { PACKAGE_PIN M17   IOSTANDARD LVCMOS33 } [get_ports { ja[5] }]; #IO_L7N_T1_D10_14 Sch=ja_n[3]
#set_property -dict { PACKAGE_PIN M18   IOSTANDARD LVCMOS33 } [get_ports { ja[6] }]; #IO_L8P_T1_D11_14 Sch=ja_p[4]
#set_property -dict { PACKAGE_PIN N18   IOSTANDARD LVCMOS33 } [get_ports { ja[7] }]; #IO_L8N_T1_D12_14 Sch=ja_n[4]

## Pmod Header JB
#set_property -dict { PACKAGE_PIN P17   IOSTANDARD LVCMOS33 } [get_ports { jb[0] }]; #IO_L9P_T1_DQS_14 Sch=jb_p[1]
#set_property -dict { PACKAGE_PIN P18   IOSTANDARD LVCMOS33 } [get_ports { jb[1] }]; #IO_L9N_T1_DQS_D13_14 Sch=jb_n[1]
#set_property -dict { PACKAGE_PIN R18   IOSTANDARD LVCMOS33 } [get_ports { jb[2] }]; #IO_L10P_T1_D14_14 Sch=jb_p[2]
#set_property -dict { PACKAGE_PIN T18   IOSTANDARD LVCMOS33 } [get_ports { jb[3] }]; #IO_L10N_T1_D15_14 Sch=jb_n[2]
#set_property -dict { PACKAGE_PIN P14   IOSTANDARD LVCMOS33 } [get_ports { jb[4] }]; #IO_L11P_T1_SRCC_14 Sch=jb_p[3]
#set_property -dict { PACKAGE_PIN P15   IOSTANDARD LVCMOS33 } [get_ports { jb[5] }]; #IO_L11N_T1_SRCC_14 Sch=jb_n[3]
#set_property -dict { PACKAGE_PIN N15   IOSTANDARD LVCMOS33 } [get_ports { jb[6] }]; #IO_L12P_T1_MRCC_14 Sch=jb_p[4]
#set_property -dict { PACKAGE_PIN P16   IOSTANDARD LVCMOS33 } [get_ports { jb[7] }]; #IO_L12N_T1_MRCC_14 Sch=jb_n[4]

## Pmod Header JC
#set_property -dict { PACKAGE_PIN U15   IOSTANDARD LVCMOS33 } [get_ports { jc[0] }]; #IO_L18P_T2_A12_D28_14 Sch=jc1/ck_io[41]
#set_property -dict { PACKAGE_PIN V16   IOSTANDARD LVCMOS33 } [get_ports { jc[1] }]; #IO_L18N_T2_A11_D27_14 Sch=jc2/ck_io[40]
#set_property -dict { PACKAGE_PIN U17   IOSTANDARD LVCMOS33 } [get_ports { jc[2] }]; #IO_L15P_T2_DQS_RDWR_B_14 Sch=jc3/ck_io[39]
#set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports { jc[3] }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=jc4/ck_io[38]
#set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports { jc[4] }]; #IO_L16P_T2_CSI_B_14 Sch=jc7/ck_io[37]
#set_property -dict { PACKAGE_PIN P13   IOSTANDARD LVCMOS33 } [get_ports { jc[5] }]; #IO_L19P_T3_A10_D26_14 Sch=jc8/ck_io[36]
#set_property -dict { PACKAGE_PIN R13   IOSTANDARD LVCMOS33 } [get_ports { jc[6] }]; #IO_L19N_T3_A09_D25_VREF_14 Sch=jc9/ck_io[35]
#set_property -dict { PACKAGE_PIN V14   IOSTANDARD LVCMOS33 } [get_ports { jc[7] }]; #IO_L20P_T3_A08_D24_14 Sch=jc10/ck_io[34]

## Pmod Header JD
#set_property -dict { PACKAGE_PIN V15   IOSTANDARD LVCMOS33 } [get_ports { jd[0] }]; #IO_L20N_T3_A07_D23_14 Sch=jd1/ck_io[33]
#set_property -dict { PACKAGE_PIN U12   IOSTANDARD LVCMOS33 } [get_ports { jd[1] }]; #IO_L21P_T3_DQS_14 Sch=jd2/ck_io[32]
#set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports { jd[2] }]; #IO_L21N_T3_DQS_A06_D22_14 Sch=jd3/ck_io[31]
#set_property -dict { PACKAGE_PIN T12   IOSTANDARD LVCMOS33 } [get_ports { jd[3] }]; #IO_L22P_T3_A05_D21_14 Sch=jd4/ck_io[30]
#set_property -dict { PACKAGE_PIN T13   IOSTANDARD LVCMOS33 } [get_ports { jd[4] }]; #IO_L22N_T3_A04_D20_14 Sch=jd7/ck_io[29]
#set_property -dict { PACKAGE_PIN R11   IOSTANDARD LVCMOS33 } [get_ports { jd[5] }]; #IO_L23P_T3_A03_D19_14 Sch=jd8/ck_io[28]
#set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { jd[6] }]; #IO_L23N_T3_A02_D18_14 Sch=jd9/ck_io[27]
#set_property -dict { PACKAGE_PIN U11   IOSTANDARD LVCMOS33 } [get_ports { jd[7] }]; #IO_L24P_T3_A01_D17_14 Sch=jd10/ck_io[26]

## USB-UART Interface
#set_property -dict { PACKAGE_PIN R12   IOSTANDARD LVCMOS33 } [get_ports { uart_rxd_out }]; #IO_25_14 Sch=uart_rxd_out
#set_property -dict { PACKAGE_PIN V12   IOSTANDARD LVCMOS33 } [get_ports { uart_txd_in }]; #IO_L24N_T3_A00_D16_14 Sch=uart_txd_in

## ChipKit Outer Digital Header
# Right Motor Control
set_property -dict { PACKAGE_PIN L13   IOSTANDARD LVCMOS33 } [get_ports { pwm0_0 }]; #IO_0_14 Sch=ck_io[0]
set_property -dict { PACKAGE_PIN N13   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[0] }]; #IO_L6N_T0_D08_VREF_14   Sch=ck_io[1]
set_property -dict { PACKAGE_PIN L16   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[1] }]; #IO_L3N_T0_DQS_EMCCLK_14 Sch=ck_io[2]
# Left motor Control
set_property -dict { PACKAGE_PIN R14   IOSTANDARD LVCMOS33 } [get_ports { pwm0_1 }]; #IO_L13P_T2_MRCC_14      Sch=ck_io[3]
set_property -dict { PACKAGE_PIN T14   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[2] }]; #IO_L13N_T2_MRCC_14      Sch=ck_io[4]
set_property -dict { PACKAGE_PIN R16   IOSTANDARD LVCMOS33 } [get_ports { hbridge_control_tri_o[3] }]; #IO_L14P_T2_SRCC_14      Sch=ck_io[5]

#set_property -dict { PACKAGE_PIN R17   IOSTANDARD LVCMOS33 } [get_ports { ck_io6 }]; #IO_L14N_T2_SRCC_14      Sch=ck_io[6]
#set_property -dict { PACKAGE_PIN V17   IOSTANDARD LVCMOS33 } [get_ports { ck_io7 }]; #IO_L16N_T2_A15_D31_14   Sch=ck_io[7]
#set_property -dict { PACKAGE_PIN R15   IOSTANDARD LVCMOS33 } [get_ports { ck_io8 }]; #IO_L17P_T2_A14_D30_14   Sch=ck_io[8]
#set_property -dict { PACKAGE_PIN T15   IOSTANDARD LVCMOS33 } [get_ports { ck_io9 }]; #IO_L17N_T2_A13_D29_14   Sch=ck_io[9]

## ChipKit SPI Header
## NOTE: The ChipKit SPI header ports can also be used as digital I/O and share FPGA pins with ck_io10-13. Do not use both at the same time.
#set_property -dict { PACKAGE_PIN H16   IOSTANDARD LVCMOS33 } [get_ports { ck_io10_ss   }]; #IO_L22P_T3_A17_15   Sch=ck_io10_ss
#set_property -dict { PACKAGE_PIN H17   IOSTANDARD LVCMOS33 } [get_ports { ck_io11_mosi }]; #IO_L22N_T3_A16_15   Sch=ck_io11_mosi
#set_property -dict { PACKAGE_PIN K14   IOSTANDARD LVCMOS33 } [get_ports { ck_io12_miso }]; #IO_L23P_T3_FOE_B_15 Sch=ck_io12_miso
#set_property -dict { PACKAGE_PIN G16   IOSTANDARD LVCMOS33 } [get_ports { ck_io13_sck  }]; #IO_L14P_T2_SRCC_15  Sch=ck_io13_sck

## ChipKit Inner Digital Header
## Note: these pins are shared with PMOD Headers JC and JD and cannot be used at the same time as the applicable PMOD interface(s)
#set_property -dict { PACKAGE_PIN U11   IOSTANDARD LVCMOS33 } [get_ports { ck_io26 }]; #IO_L24P_T3_A01_D17_14        Sch=jd10/ck_io[26]
#set_property -dict { PACKAGE_PIN T11   IOSTANDARD LVCMOS33 } [get_ports { ck_io27 }]; #IO_L23N_T3_A02_D18_14        Sch=jd9/ck_io[27]
#set_property -dict { PACKAGE_PIN R11   IOSTANDARD LVCMOS33 } [get_ports { ck_io28 }]; #IO_L23P_T3_A03_D19_14        Sch=jd8/ck_io[28]
#set_property -dict { PACKAGE_PIN T13   IOSTANDARD LVCMOS33 } [get_ports { ck_io29 }]; #IO_L22N_T3_A04_D20_14        Sch=jd7/ck_io[29]
#set_property -dict { PACKAGE_PIN T12   IOSTANDARD LVCMOS33 } [get_ports { ck_io30 }]; #IO_L22P_T3_A05_D21_14        Sch=jd4/ck_io[30]
#set_property -dict { PACKAGE_PIN V13   IOSTANDARD LVCMOS33 } [get_ports { ck_io31 }]; #IO_L21N_T3_DQS_A06_D22_14    Sch=jd3/ck_io[31]
#set_property -dict { PACKAGE_PIN U12   IOSTANDARD LVCMOS33 } [get_ports { ck_io32 }]; #IO_L21P_T3_DQS_14            Sch=jd2/ck_io[32]
#set_property -dict { PACKAGE_PIN V15   IOSTANDARD LVCMOS33 } [get_ports { ck_io33 }]; #IO_L20N_T3_A07_D23_14        Sch=jd1/ck_io[33]
#set_property -dict { PACKAGE_PIN V14   IOSTANDARD LVCMOS33 } [get_ports { ck_io34 }]; #IO_L20P_T3_A08_D24_14        Sch=jc10/ck_io[34]
#set_property -dict { PACKAGE_PIN R13   IOSTANDARD LVCMOS33 } [get_ports { ck_io35 }]; #IO_L19N_T3_A09_D25_VREF_14   Sch=jc9/ck_io[35]
#set_property -dict { PACKAGE_PIN P13   IOSTANDARD LVCMOS33 } [get_ports { ck_io36 }]; #IO_L19P_T3_A10_D26_14        Sch=jc8/ck_io[36]
#set_property -dict { PACKAGE_PIN U16   IOSTANDARD LVCMOS33 } [get_ports { ck_io37 }]; #IO_L16P_T2_CSI_B_14          Sch=jc7/ck_io[37]
#set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports { ck_io38 }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=jc4/ck_io[38]
#set_property -dict { PACKAGE_PIN U17   IOSTANDARD LVCMOS33 } [get_ports { ck_io39 }]; #IO_L15P_T2_DQS_RDWR_B_14     Sch=jc3/ck_io[39]
#set_property -dict { PACKAGE_PIN V16   IOSTANDARD LVCMOS33 } [get_ports { ck_io40 }]; #IO_L18N_T2_A11_D27_14        Sch=jc2/ck_io[40]
#set_property -dict { PACKAGE_PIN U15   IOSTANDARD LVCMOS33 } [get_ports { ck_io41 }]; #IO_L18P_T2_A12_D28_14        Sch=jc1/ck_io[41]

## Dedicated Analog Inputs
#set_property -dict { PACKAGE_PIN J10   } [get_ports { vp_in }]; #IO_L1P_T0_AD4P_35 Sch=v_p
#set_property -dict { PACKAGE_PIN K9    } [get_ports { vn_in }]; #IO_L1N_T0_AD4N_35 Sch=v_n

## ChipKit Outer Analog Header - as Single-Ended Analog Inputs
## NOTE: These ports can be used as single-ended analog inputs with voltages from 0-3.3V (ChipKit analog pins A0-A5) or as digital I/O.
## WARNING: Do not use both sets of constraints at the same time!
## NOTE: The following constraints should be used with the XADC IP core when using these ports as analog inputs.
#set_property -dict { PACKAGE_PIN B13   IOSTANDARD LVCMOS33 } [get_ports { vaux0_p  }]; #IO_L1P_T0_AD0P_15     Sch=ck_an_p[0]   ChipKit pin=A0
#set_property -dict { PACKAGE_PIN A13   IOSTANDARD LVCMOS33 } [get_ports { vaux0_n  }]; #IO_L1N_T0_AD0N_15     Sch=ck_an_n[0]   ChipKit pin=A0
#set_property -dict { PACKAGE_PIN B15   IOSTANDARD LVCMOS33 } [get_ports { vaux1_p  }]; #IO_L3P_T0_DQS_AD1P_15 Sch=ck_an_p[1]   ChipKit pin=A1
#set_property -dict { PACKAGE_PIN A15   IOSTANDARD LVCMOS33 } [get_ports { vaux1_n  }]; #IO_L3N_T0_DQS_AD1N_15 Sch=ck_an_n[1]   ChipKit pin=A1
#set_property -dict { PACKAGE_PIN E12   IOSTANDARD LVCMOS33 } [get_ports { vaux9_p  }]; #IO_L5P_T0_AD9P_15     Sch=ck_an_p[2]   ChipKit pin=A2
#set_property -dict { PACKAGE_PIN D12   IOSTANDARD LVCMOS33 } [get_ports { vaux9_n  }]; #IO_L5N_T0_AD9N_15     Sch=ck_an_n[2]   ChipKit pin=A2
#set_property -dict { PACKAGE_PIN B17   IOSTANDARD LVCMOS33 } [get_ports { vaux2_p  }]; #IO_L7P_T1_AD2P_15     Sch=ck_an_p[3]   ChipKit pin=A3
#set_property -dict { PACKAGE_PIN A17   IOSTANDARD LVCMOS33 } [get_ports { vaux2_n  }]; #IO_L7N_T1_AD2N_15     Sch=ck_an_n[3]   ChipKit pin=A3
#set_property -dict { PACKAGE_PIN C17   IOSTANDARD LVCMOS33 } [get_ports { vaux10_p }]; #IO_L8P_T1_AD10P_15    Sch=ck_an_p[4]   ChipKit pin=A4
#set_property -dict { PACKAGE_PIN B18   IOSTANDARD LVCMOS33 } [get_ports { vaux10_n }]; #IO_L8N_T1_AD10N_15    Sch=ck_an_n[4]   ChipKit pin=A4
#set_property -dict { PACKAGE_PIN E16   IOSTANDARD LVCMOS33 } [get_ports { vaux11_p }]; #IO_L10P_T1_AD11P_15   Sch=ck_an_p[5]   ChipKit pin=A5
#set_property -dict { PACKAGE_PIN E17   IOSTANDARD LVCMOS33 } [get_ports { vaux11_n }]; #IO_L10N_T1_AD11N_15   Sch=ck_an_n[5]   ChipKit pin=A5
## ChipKit Outer Analog Header - as Digital I/O
## NOTE: The following constraints should be used when using these ports as digital I/O.
#set_property -dict { PACKAGE_PIN G13   IOSTANDARD LVCMOS33 } [get_ports { ck_a0 }]; #IO_0_15            Sch=ck_a[0]
#set_property -dict { PACKAGE_PIN B16   IOSTANDARD LVCMOS33 } [get_ports { ck_a1 }]; #IO_L4P_T0_15       Sch=ck_a[1]
#set_property -dict { PACKAGE_PIN A16   IOSTANDARD LVCMOS33 } [get_ports { ck_a2 }]; #IO_L4N_T0_15       Sch=ck_a[2]
#set_property -dict { PACKAGE_PIN C13   IOSTANDARD LVCMOS33 } [get_ports { ck_a3 }]; #IO_L6P_T0_15       Sch=ck_a[3]
#set_property -dict { PACKAGE_PIN C14   IOSTANDARD LVCMOS33 } [get_ports { ck_a4 }]; #IO_L6N_T0_VREF_15  Sch=ck_a[4]
#set_property -dict { PACKAGE_PIN D18   IOSTANDARD LVCMOS33 } [get_ports { ck_a5 }]; #IO_L11P_T1_SRCC_15 Sch=ck_a[5]

## ChipKit Inner Analog Header - as Differential Analog Inputs
## NOTE: These ports can be used as differential analog inputs with voltages from 0-1.0V (ChipKit analog pins A6-A11) or as digital I/O.
## WARNING: Do not use both sets of constraints at the same time!
## NOTE: The following constraints should be used with the XADC core when using these ports as analog inputs.
#set_property -dict { PACKAGE_PIN B14   IOSTANDARD LVCMOS33 } [get_ports { vaux8_p }]; #IO_L2P_T0_AD8P_15     Sch=ad_p[8]   ChipKit pin=A6
#set_property -dict { PACKAGE_PIN A14   IOSTANDARD LVCMOS33 } [get_ports { vaux8_n }]; #IO_L2N_T0_AD8N_15     Sch=ad_n[8]   ChipKit pin=A7
#set_property -dict { PACKAGE_PIN D16   IOSTANDARD LVCMOS33 } [get_ports { vaux3_p }]; #IO_L9P_T1_DQS_AD3P_15 Sch=ad_p[3]   ChipKit pin=A8
#set_property -dict { PACKAGE_PIN D17   IOSTANDARD LVCMOS33 } [get_ports { vaux3_n }]; #IO_L9N_T1_DQS_AD3N_15 Sch=ad_n[3]   ChipKit pin=A9
## ChipKit Inner Analog Header - as Digital I/O
## NOTE: The following constraints should be used when using the inner analog header ports as digital I/O.
#set_property -dict { PACKAGE_PIN B14   IOSTANDARD LVCMOS33 } [get_ports { ck_a6  }]; #IO_L2P_T0_AD8P_15     Sch=ad_p[8]
#set_property -dict { PACKAGE_PIN A14   IOSTANDARD LVCMOS33 } [get_ports { ck_a7  }]; #IO_L2N_T0_AD8N_15     Sch=ad_n[8]
#set_property -dict { PACKAGE_PIN D16   IOSTANDARD LVCMOS33 } [get_ports { ck_a8  }]; #IO_L9P_T1_DQS_AD3P_15 Sch=ad_p[3]
#set_property -dict { PACKAGE_PIN D17   IOSTANDARD LVCMOS33 } [get_ports { ck_a9  }]; #IO_L9N_T1_DQS_AD3N_15 Sch=ad_n[3]
#set_property -dict { PACKAGE_PIN D14   IOSTANDARD LVCMOS33 } [get_ports { ck_a10 }]; #IO_L12P_T1_MRCC_15    Sch=ck_a10_r   (Cannot be used as an analog input)
#set_property -dict { PACKAGE_PIN D15   IOSTANDARD LVCMOS33 } [get_ports { ck_a11 }]; #IO_L12N_T1_MRCC_15    Sch=ck_a11_r   (Cannot be used as an analog input)

## ChipKit I2C
#set_property -dict { PACKAGE_PIN J14   IOSTANDARD LVCMOS33 } [get_ports { ck_scl }]; #IO_L24N_T3_RS0_15 Sch=ck_scl
#set_property -dict { PACKAGE_PIN J13   IOSTANDARD LVCMOS33 } [get_ports { ck_sda }]; #IO_L24P_T3_RS1_15 Sch=ck_sda

## Misc. ChipKit Ports
#set_property -dict { PACKAGE_PIN K13   IOSTANDARD LVCMOS33 } [get_ports { ck_ioa }]; #IO_25_15 Sch=ck_ioa
#set_property -dict { PACKAGE_PIN C18   IOSTANDARD LVCMOS33 } [get_ports { ck_rst }]; #IO_L11N_T1_SRCC_15

## Quad SPI Flash
## Note: the SCK clock signal can be driven using the STARTUPE2 primitive
#set_property -dict { PACKAGE_PIN M13   IOSTANDARD LVCMOS33 } [get_ports { qspi_cs }]; #IO_L6P_T0_FCS_B_14 Sch=qspi_cs
#set_property -dict { PACKAGE_PIN K17   IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[0] }]; #IO_L1P_T0_D00_MOSI_14 Sch=qspi_dq[0]
#set_property -dict { PACKAGE_PIN K18   IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[1] }]; #IO_L1N_T0_D01_DIN_14 Sch=qspi_dq[1]
#set_property -dict { PACKAGE_PIN L14   IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[2] }]; #IO_L2P_T0_D02_14 Sch=qspi_dq[2]
#set_property -dict { PACKAGE_PIN M15   IOSTANDARD LVCMOS33 } [get_ports { qspi_dq[3] }]; #IO_L2N_T0_D03_14 Sch=qspi_dq[3]

## Configuration options, can be used for all designs
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
set_property CFGBVS VCCO [current_design]
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]

## SW3 is assigned to a pin M5 in the 1.35v bank. This pin can also be used as
## the VREF for BANK 34. To ensure that SW3 does not define the reference voltage
## and to be able to use this pin as an ordinary I/O the following property must
## be set to enable an internal VREF for BANK 34. Since a 1.35v supply is being
## used the internal reference is set to half that value (i.e. 0.675v). Note that
## this property must be set even if SW3 is not used in the design.
set_property INTERNAL_VREF 0.675 [get_iobanks 34]


image

  • Create an HDL Wrapper
  • Generate Bitstream
  • Export a Fixed Post-Synthesis Hardware Platform including bitstream

References: AXI Timer/Counter (xilinx.com)


PWM Test Software with Xilinx SDK for C/C++

To test our design we are going to configure one AXI Timer to produce a PWM signal with a 500 ms duty cycle and a 25% on time.

  • From Vitis we create a new application project.
  • Create a new platform with the xsa file
  • Create empty application
  • Copy the following code in a new file:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"

#include "xtmrctr.h"

#define TMRCTR_DEVICE_ID        XPAR_TMRCTR_0_DEVICE_ID
#define PWM_PERIOD              500000000    /* PWM period in (500 ms) */
#define TMRCTR_0                0            /* Timer 0 ID */
#define TMRCTR_1                1            /* Timer 1 ID */
#define CYCLE_PER_DUTYCYCLE     10           /* Clock cycles per duty cycle */
#define MAX_DUTYCYCLE           100          /* Max duty cycle */
#define DUTYCYCLE_DIVISOR       4            /* Duty cycle Divisor */

XTmrCtr TimerCounterInst;

int main()
{
	u8  divisor;
	u32 period;
	u32 highTime;
	int status;
    init_platform();

    print("Initializing the Timer Counter...");
	/*
	 * Initialize the timer counter so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	 */
	status = XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
	if (status != XST_SUCCESS) {
		return XST_FAILURE;
	}


    print("Timer Counter initialized");
    divisor = DUTYCYCLE_DIVISOR;

    /* Disable PWM for reconfiguration */
    XTmrCtr_PwmDisable(&TimerCounterInst);

	/* Configure PWM */
    period = PWM_PERIOD;
	highTime = PWM_PERIOD / divisor;

	XTmrCtr_PwmConfigure(&TimerCounterInst, period, highTime);

	/* Enable PWM after reconfiguration */
	XTmrCtr_PwmEnable(&TimerCounterInst);
	while(1){

	}

    cleanup_platform();
    return 0;
}

The use and configuration is very simple:

#include "xtmrctr.h"

  • Define the timer instance

XTmrCtr TimerCounterInst;

  • Initialize the timer counter so that it's ready to use,

XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID); 

  • Disable PWM for reconfiguration

XTmrCtr_PwmDisable(&TimerCounterInst); 

  • Configure PWM 

XTmrCtr_PwmConfigure(&TimerCounterInst, period, highTime); 

  • Enable PWM after reconfiguration

XTmrCtr_PwmEnable(&TimerCounterInst); 


Debugging with the Logic Analyzer of the Digilent Analog Discovery 2 

We will use the Logic Analyzer from the Digilent Analog Discovery 2 to check our solution.

The core of the Analog Discovery 2 is the Xilinx® Spartan®-6 FPGA (specifically, the XC6SLX16-1L device). The Digital I/O block exposes protected access to the FPGA pins assigned for the Digital Pattern Generator and Logic Analyzer. General purpose FPGA I/O pins are used for Analog Discovery 2 Digital I/O. FPGA pins are set to SLOW slew rate and 4mA drive strength, with no internal pull. Input and output pins are LVCMOS3V3. Inputs are 5V tolerant. Overvoltage up to ±20V is supported.

image

Outputs from the Digilent Board are connected to the Analog Discovery 2 Digital I/O Pins

ArtyBot PWM output


Incrementing and Decrementing Duty Cycle in real time

We are going to complicate the software a bit and this time we are going to allow you to change the Duty cycle with two buttons, one to increase the duty cycle and the other to decrease it. A third panic button will allow us to exit the application in a friendly way.

Copy this new Source code:

/*
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xtmrctr.h"
#include "xgpio.h"
#include "xil_types.h"

#define TMRCTR_DEVICE_ID        XPAR_TMRCTR_0_DEVICE_ID
#define PWM_PERIOD              10000000     /* PWM period in (10 ms) */
#define TMRCTR_0                0            /* Timer 0 ID */
#define TMRCTR_1                1            /* Timer 1 ID */
#define CYCLE_PER_DUTYCYCLE     10           /* Clock cycles per duty cycle */
#define MAX_DUTYCYCLE           100          /* Max duty cycle */
#define DUTYCYCLE_DIVISOR       MAX_DUTYCYCLE/* Duty cycle Divisor */

#define PERCENTAGE_INCREMENTS   10

#define BTN_MASK 0b1111
#define LED_MASK 0b1111
#define BTN1_MASK 0b0001
#define BTN2_MASK 0b0010
#define BTN3_MASK 0b0100
#define BTN4_MASK 0b1000
#define LED1_MASK 0b0001
#define LED2_MASK 0b0010
#define LED3_MASK 0b0100
#define LED4_MASK 0b1000

// Get device IDs from xparameters.h
#define BTN_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define LED_ID XPAR_AXI_GPIO_1_DEVICE_ID
#define BTN_CHANNEL 1
#define LED_CHANNEL 2

XGpio_Config *cfg_ptr;
XGpio led_device, btn_device;

XTmrCtr TimerCounterInst;

void setPwm_DutyCyclePercentage(XTmrCtr* TimerCounterInst,
		u8 percentage) {
	u32 period;
	u32 highTime;
	if (percentage >= DUTYCYCLE_DIVISOR) {
		percentage = 99;
	} else if (percentage < 0) {
		percentage = 0;
	}
	/* Disable PWM for reconfiguration if percentage == 0 disable pwm */
	XTmrCtr_PwmDisable(&*TimerCounterInst);
	/* Configure PWM */
	if (percentage > 0) {
		period = PWM_PERIOD;
		highTime = (period / DUTYCYCLE_DIVISOR) * percentage ;
		XTmrCtr_PwmConfigure(&*TimerCounterInst, period, highTime);
		/* Enable PWM after reconfiguration */
		XTmrCtr_PwmEnable(&*TimerCounterInst);
	}
}

int main() {
	u8 percentageLeftMotor;
	int status;

	u64 timeCounterMillis;
	u32 buttonsState;
	int button1State;
	int lastbutton1State;

	int button2State;
	int lastbutton2State;

	init_platform();

	print("Initializing LED Device...\r\n");

	// Initialize LED Device
	cfg_ptr = XGpio_LookupConfig(LED_ID);
	XGpio_CfgInitialize(&led_device, cfg_ptr, cfg_ptr->BaseAddress);

	print("Initializing the Button Device\r\n");

	// Initialize Button Device
	cfg_ptr = XGpio_LookupConfig(BTN_ID);
	XGpio_CfgInitialize(&btn_device, cfg_ptr, cfg_ptr->BaseAddress);

	// Set Button Tristate
	XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK);

	// Set Led Tristate
	XGpio_SetDataDirection(&led_device, LED_CHANNEL, 0);

	print("Initializing the Timer Counter...\r\n");
	/*
	 * Initialize the timer counter so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	 */
	status = XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
	if (status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	print("Timer Counter initialized\r\n");
	percentageLeftMotor = 50;


	/* Disable PWM for reconfiguration */
	setPwm_DutyCyclePercentage(&TimerCounterInst, percentageLeftMotor);

	while (1) {

		// read buttons
		buttonsState = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL);

		//if button 4 is pressed exit
		if (buttonsState & BTN4_MASK) {
			print("exiting\r\n");
			// exit
			break;
		}

		// check button 1 for incrementing duty dycle percentage for left motor
		button1State = buttonsState & BTN1_MASK;
		//if button 1 low to high edge
		if ((button1State != lastbutton1State) && button1State && timeCounterMillis > 100) {
			print("Increment duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageLeftMotor < 100) {
				percentageLeftMotor = percentageLeftMotor + PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&TimerCounterInst, percentageLeftMotor);
			}
		}
		lastbutton1State = button1State;

		// check button 2 for decrementing duty dycle percentage for left motor
		button2State = buttonsState & BTN2_MASK;
		//if button 2 low to high edge
		if ((button2State != lastbutton2State) && button2State && timeCounterMillis > 100) {
			print("Decrement duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageLeftMotor > 0) {
				percentageLeftMotor = percentageLeftMotor - PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&TimerCounterInst, percentageLeftMotor);
			}
		}
		lastbutton2State = button2State;

		usleep(1000);
		++timeCounterMillis;

	}

	cleanup_platform();
	return 0;
}

Debugging the PWM Signal with the Logic Analyzer of the Analog Discovery 2

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

image


Testing with the Motor Driver and Power Distribution Board

We have verified that the voltage levels and the PWM signal is correct and that the program responds correctly to the buttons.

It is time to connect the Arty S7 board with the motor driver and power distribution board.

See the video above, in the introduction section of this blog.

image

And finally with the two PWM signals activated controlled by the four push buttons.

/*
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "sleep.h"
#include "xtmrctr.h"
#include "xgpio.h"
#include "xil_types.h"

#define RIGHTMOTORTMRCTR_DEVICE_ID        XPAR_TMRCTR_0_DEVICE_ID
#define LEFTMOTORTMRCTR_DEVICE_ID         XPAR_TMRCTR_1_DEVICE_ID
#define PWM_PERIOD              10000000     /* PWM period in (10 ms) */
#define TMRCTR_0                0            /* Timer 0 ID */
#define TMRCTR_1                1            /* Timer 1 ID */
#define CYCLE_PER_DUTYCYCLE     10           /* Clock cycles per duty cycle */
#define MAX_DUTYCYCLE           100          /* Max duty cycle */
#define DUTYCYCLE_DIVISOR       MAX_DUTYCYCLE/* Duty cycle Divisor */

#define PERCENTAGE_INCREMENTS   10

#define BTN_MASK 0b1111
#define LED_MASK 0b1111
#define BTN1_MASK 0b0001
#define BTN2_MASK 0b0010
#define BTN3_MASK 0b0100
#define BTN4_MASK 0b1000
#define LED1_MASK 0b0001
#define LED2_MASK 0b0010
#define LED3_MASK 0b0100
#define LED4_MASK 0b1000

// Get device IDs from xparameters.h
#define BTN_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define LED_ID XPAR_AXI_GPIO_1_DEVICE_ID
#define BTN_CHANNEL 1
#define LED_CHANNEL 2

XGpio_Config *cfg_ptr;
XGpio led_device, btn_device;

XTmrCtr RightMotorTimer;
XTmrCtr LeftMotorTimer;

void setPwm_DutyCyclePercentage(XTmrCtr* MotorTimer,
		u8 percentage) {
	u32 period;
	u32 highTime;
	if (percentage >= DUTYCYCLE_DIVISOR) {
		percentage = 99;
	} else if (percentage < 0) {
		percentage = 0;
	}
	/* Disable PWM for reconfiguration if percentage == 0 disable pwm */
	XTmrCtr_PwmDisable(&*MotorTimer);
	/* Configure PWM */
	if (percentage > 0) {
		period = PWM_PERIOD;
		highTime = (period / DUTYCYCLE_DIVISOR) * percentage ;
		XTmrCtr_PwmConfigure(&*MotorTimer, period, highTime);
		/* Enable PWM after reconfiguration */
		XTmrCtr_PwmEnable(&*MotorTimer);
	}
}

int main() {
	u8 percentageRightMotor;
	u8 percentageLeftMotor;
	int status;

	u64 timeCounterMillis;
	u32 buttonsState;

	int button1State;
	int lastbutton1State = 0;

	int button2State;
	int lastbutton2State = 0;

	int button3State;
	int lastbutton3State = 0;

	int button4State;
	int lastbutton4State = 0;

	init_platform();

	print("Initializing LED Device...\r\n");

	// Initialize LED Device
	cfg_ptr = XGpio_LookupConfig(LED_ID);
	XGpio_CfgInitialize(&led_device, cfg_ptr, cfg_ptr->BaseAddress);

	print("Initializing the Button Device\r\n");

	// Initialize Button Device
	cfg_ptr = XGpio_LookupConfig(BTN_ID);
	XGpio_CfgInitialize(&btn_device, cfg_ptr, cfg_ptr->BaseAddress);

	// Set Button Tristate
	XGpio_SetDataDirection(&btn_device, BTN_CHANNEL, BTN_MASK);

	// Set Led Tristate
	XGpio_SetDataDirection(&led_device, LED_CHANNEL, 0);

	print("Initializing the Timer Counter...\r\n");
	/*
	 * Initialize the timer counter so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	 */
	status = XTmrCtr_Initialize(&RightMotorTimer, RIGHTMOTORTMRCTR_DEVICE_ID);


	if (status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	status = XTmrCtr_Initialize(&LeftMotorTimer, LEFTMOTORTMRCTR_DEVICE_ID);


	if (status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	print("Timer Counter initialized\r\n");
	percentageRightMotor = 0;
	percentageLeftMotor = 0;

	setPwm_DutyCyclePercentage(&RightMotorTimer, percentageRightMotor);
	setPwm_DutyCyclePercentage(&LeftMotorTimer, percentageLeftMotor);

	while (1) {

		// read buttons
		buttonsState = XGpio_DiscreteRead(&btn_device, BTN_CHANNEL);

		// check button 1 for incrementing duty dycle percentage for Right motor
		button1State = buttonsState & BTN1_MASK;
		//if button 1 low to high edge
		if ((button1State != lastbutton1State) && button1State && timeCounterMillis > 100) {
			print("Increment Right Motor duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageRightMotor < 100) {
				percentageRightMotor = percentageRightMotor + PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&RightMotorTimer, percentageRightMotor);
			}
		}
		lastbutton1State = button1State;

		// check button 2 for decrementing duty dycle percentage for Right motor
		button2State = buttonsState & BTN2_MASK;
		//if button 2 low to high edge
		if ((button2State != lastbutton2State) && button2State && timeCounterMillis > 100) {
			print("Decrement Right Motor duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageRightMotor > 0) {
				percentageRightMotor = percentageRightMotor - PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&RightMotorTimer, percentageRightMotor);
			}
		}
		lastbutton2State = button2State;

		// check button 3 for incrementing duty dycle percentage for Left motor
		button3State = buttonsState & BTN3_MASK;
		//if button 3 low to high edge
		if ((button3State != lastbutton3State) && button3State && timeCounterMillis > 100) {
			print("Increment Left Motor duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageLeftMotor < 100) {
				percentageLeftMotor = percentageLeftMotor + PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&LeftMotorTimer, percentageLeftMotor);
			}
		}
		lastbutton3State = button3State;

		// check button 4 for decrementing duty dycle percentage for Left motor
		button4State = buttonsState & BTN4_MASK;
		if ((button4State != lastbutton4State) && button4State && timeCounterMillis > 100) {
			print("Decrement Left Motor duty cycle units\r\n");
			timeCounterMillis = 0;
			if ( percentageLeftMotor > 0) {
				percentageLeftMotor = percentageLeftMotor - PERCENTAGE_INCREMENTS;
				setPwm_DutyCyclePercentage(&LeftMotorTimer, percentageLeftMotor);
			}
		}
		lastbutton4State = button4State;





		usleep(1000);
		++timeCounterMillis;

	}

	cleanup_platform();
	return 0;
}

image


ArtyBot ready for the next adventure.

We already know how to control the speed of the ArtyBot's motors. Our robot is ready to start discovering and sensing the world.

ArtyBot


References

  • Classic TI-RSLK Maze Edition Curriculum
  • Pololu - Motor Driver and Power Distribution Board for Romi Chassis
  • Pololu - Romi Chassis Kit - Red
  • DRV8838 data sheet, product information and support | TI.com
  • DRV883x Low-Voltage H-Bridge Driver datasheet (Rev. F) (ti.com)
  • arty_s7_sch-rev_b.pdf(digilent.com)
  • Arty S7 Reference Manual - Digilent Reference
  • Pulse-width modulation - Wikipedia
  • AXI Timer/Counter (xilinx.com)
  • digilent-xdc/Arty-S7-50-Master.xdc at master · Digilent/digilent-xdc (github.com)


"7 Ways to Leave Your Spartan-6" related posts
1 Arty S7 50 First Power Up and Hardware-Only Blinky
2 Arty S7 50 First Baremetal Software Project
3 Arty S7 50 Rapid Prototyping - Environmental Monitor
4 Arty S7 50 ArtyBot Pulse Width Modulation (PWM) Motor Speed Control
5 Arty S7 50 ArtyBot Custom AXI4 Lite IP Peripheral for Sensing Motor Rotational Speed
6 Arty S7 50 ArtyBot How to Store MicroBlaze Program in the Quad-SPI Flash from Vivado
7 Arty S7 50 ArtyBot - Bot Application Framework
8 Arty S7 50 ArtyBot becomes Emubot, an educational robot for young children
9 Arty S7 50 ArtyBot - Color sensing and line follower
10 Arty S7 50 The Spartan-6 Migration Path Game. Learning the Differences Between Spartan-6 and Spartan-7 FPGAs
11 Arty S7 50 ArtyBot ToF Sensor for Obstacle Avoidance. Vivado Hierarchical Blocks
12 "7 Ways to Leave Your Spartan-6 FPGA" program. My summary
13 Arty S7 50 VGA Thermal Imaging Camera
  • Sign in to reply
  • saadtiwana_int
    saadtiwana_int over 2 years ago

    Nice, detailed write-up. Thanks for sharing!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • maxpowerr
    maxpowerr over 2 years ago

    Good project

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube