Added USART support (Up to three USARTs)

This commit is contained in:
eddyem 2014-07-23 17:42:12 +04:00
parent 60fa061829
commit 7ebfa0fcb0
10 changed files with 455 additions and 28 deletions

View File

@ -20,11 +20,14 @@
#include "cdcacm.h" #include "cdcacm.h"
#include "user_proto.h" #include "user_proto.h"
#include "main.h"
#include "uart.h"
// Buffer for USB Tx // Buffer for USB Tx
static uint8_t USB_Tx_Buffer[USB_TX_DATA_SIZE]; static uint8_t USB_Tx_Buffer[USB_TX_DATA_SIZE];
static uint8_t USB_Tx_ptr = 0; static uint8_t USB_Tx_ptr = 0;
// connection flag
uint8_t USB_connected = 0;
static const struct usb_device_descriptor dev = { static const struct usb_device_descriptor dev = {
.bLength = USB_DT_DEVICE_SIZE, .bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE, .bDescriptorType = USB_DT_DEVICE,
@ -176,6 +179,11 @@ struct usb_cdc_line_coding linecoding = {
/* Buffer to be used for control requests. */ /* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128]; uint8_t usbd_control_buffer[128];
/**
* This function runs every time it gets a request for control parameters get/set
* parameter SET_LINE_CODING used to change USART1 parameters: if you want to
* change them, just connect through USB with required parameters
*/
static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf,
uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req)) uint16_t *len, void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
{ {
@ -183,9 +191,20 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
(void)buf; (void)buf;
(void)usbd_dev; (void)usbd_dev;
char local_buf[10]; char local_buf[10];
struct usb_cdc_line_coding lc;
switch (req->bRequest) { switch (req->bRequest) {
case SET_CONTROL_LINE_STATE:{ case SET_CONTROL_LINE_STATE:{
//P("SET_CONTROL_LINE_STATE\r\n", uart1_send);
//print_int(req->wValue, uart1_send);
//newline(uart1_send);
if(req->wValue){ // terminal is opened
USB_connected = 1;
//P("\r\n\tUSB connected!\r\n", uart1_send);
}else{ // terminal is closed
USB_connected = 0;
//P("\r\n\tUSB disconnected!\r\n", uart1_send);
}
/* /*
* This Linux cdc_acm driver requires this to be implemented * This Linux cdc_acm driver requires this to be implemented
* even though it's optional in the CDC spec, and we don't * even though it's optional in the CDC spec, and we don't
@ -203,14 +222,28 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
usbd_ep_write_packet(usbd_dev, 0x83, local_buf, 10); usbd_ep_write_packet(usbd_dev, 0x83, local_buf, 10);
}break; }break;
case SET_LINE_CODING: case SET_LINE_CODING:
if (*len != sizeof(struct usb_cdc_line_coding)) //P("SET_LINE_CODING, len=", uart1_send);
if (!len || (*len != sizeof(struct usb_cdc_line_coding)))
return 0; return 0;
memcpy((void *)&linecoding, (void *)*buf, *len); //print_int(*len, uart1_send);
//newline(uart1_send);
memcpy((void *)&lc, (void *)*buf, *len);
// Mark & Space parity don't support by hardware, check it
if(lc.bParityType == USB_CDC_MARK_PARITY || lc.bParityType == USB_CDC_SPACE_PARITY){
return 0; // error
}else{
memcpy((void *)&linecoding, (void *)&lc, sizeof(struct usb_cdc_line_coding));
UART_setspeed(USART1, &linecoding);
}
break; break;
case GET_LINE_CODING: case GET_LINE_CODING: // return linecoding buffer
usbd_ep_write_packet(usbd_dev, 0x82, (char*)&linecoding, sizeof(linecoding)); if(len && *len == sizeof(struct usb_cdc_line_coding))
memcpy((void *)*buf, (void *)&linecoding, sizeof(struct usb_cdc_line_coding));
//usbd_ep_write_packet(usbd_dev, 0x83, (char*)&linecoding, sizeof(linecoding));
//P("GET_LINE_CODING\r\n", uart1_send);
break; break;
default: default:
//P("UNKNOWN\r\n", uart1_send);
return 0; return 0;
} }
return 1; return 1;
@ -218,10 +251,8 @@ static int cdcacm_control_request(usbd_device *usbd_dev, struct usb_setup_data *
static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep){ static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep){
(void)ep; (void)ep;
char buf[64]; char buf[64];
int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64); int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);
if(len > 0) parce_incoming_buf(buf, len, usb_send); if(len > 0) parce_incoming_buf(buf, len, usb_send);
} }
@ -263,12 +294,16 @@ usbd_device *USB_init(){
* @param byte - a byte to put into a buffer * @param byte - a byte to put into a buffer
*/ */
void usb_send(uint8_t byte){ void usb_send(uint8_t byte){
if(!current_usb) return; //if(!USB_connected) return;
USB_Tx_Buffer[USB_Tx_ptr++] = byte; USB_Tx_Buffer[USB_Tx_ptr++] = byte;
if(USB_Tx_ptr == USB_TX_DATA_SIZE) // buffer can be overflowed - send it! if(USB_Tx_ptr == USB_TX_DATA_SIZE) // buffer can be overflowed - send it!
usb_send_buffer(); usb_send_buffer();
} }
/**
* Send all data in buffer over USB
* this function runs when buffer is full or on SysTick
*/
void usb_send_buffer(){ void usb_send_buffer(){
if(USB_Tx_ptr){ if(USB_Tx_ptr){
if(current_usb) if(current_usb)

View File

@ -23,12 +23,7 @@
#ifndef __CCDCACM_H__ #ifndef __CCDCACM_H__
#define __CCDCACM_H__ #define __CCDCACM_H__
#include <stdlib.h>
#include <string.h> // memcpy
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/usb/usbd.h> #include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>
// commands through EP0 // commands through EP0
#define SEND_ENCAPSULATED_COMMAND 0x00 #define SEND_ENCAPSULATED_COMMAND 0x00
@ -44,6 +39,9 @@
// Size of buffer to output // Size of buffer to output
#define USB_TX_DATA_SIZE 64 #define USB_TX_DATA_SIZE 64
// USB connection flag
extern uint8_t USB_connected;
extern struct usb_cdc_line_coding linecoding;
usbd_device *USB_init(); usbd_device *USB_init();
void usb_send(uint8_t byte); void usb_send(uint8_t byte);

View File

@ -0,0 +1,41 @@
/*
* hardware_ini.c - functions for HW initialisation
*
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "main.h"
#include "hardware_ini.h"
/**
* GPIO initialisaion: clocking + ports setup
*/
void GPIO_init(){
rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO11|GPIO12); // LED + USB
}
void SysTick_init(){
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); // Systyck: 72/8=9MHz
systick_set_reload(8999); // 9000 pulses: 1kHz
systick_interrupt_enable();
systick_counter_enable();
}

View File

@ -0,0 +1,29 @@
/*
* hardware_ini.h
*
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __HARDWARE_INI_H__
#define __HARDWARE_INI_H__
void GPIO_init();
void SysTick_init();
#endif // __HARDWARE_INI_H__

Binary file not shown.

View File

@ -20,6 +20,9 @@
*/ */
#include "main.h" #include "main.h"
#include "hardware_ini.h"
#include "cdcacm.h"
#include "uart.h"
uint32_t Timer = 0; // global timer (milliseconds) uint32_t Timer = 0; // global timer (milliseconds)
@ -29,24 +32,27 @@ int main(){
usbd_device *usbd_dev; usbd_device *usbd_dev;
//rcc_clock_setup_in_hsi_out_48mhz(); //rcc_clock_setup_in_hsi_out_48mhz();
// RCC clocking: 8MHz oscillator -> 72MHz system
rcc_clock_setup_in_hse_8mhz_out_72mhz(); rcc_clock_setup_in_hse_8mhz_out_72mhz();
rcc_periph_clock_enable(RCC_GPIOC);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, // GPIO
GPIO_CNF_OUTPUT_PUSHPULL, GPIO11|GPIO12); GPIO_init();
gpio_set(GPIOC, GPIO11); // turn off USB gpio_set(GPIOC, GPIO11); // turn off USB
gpio_clear(GPIOC, GPIO12); // turn on LED gpio_clear(GPIOC, GPIO12); // turn on LED
// init USART1
UART_init(USART1);
// USB
usbd_dev = USB_init(); usbd_dev = USB_init();
// wait a little and then turn on USB pullup
for (i = 0; i < 0x800000; i++) for (i = 0; i < 0x800000; i++)
__asm__("nop"); __asm__("nop");
gpio_clear(GPIOC, GPIO11); gpio_clear(GPIOC, GPIO11);
systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); // Systyck: 72/8=9MHz // SysTick is a system timer with 1mc period
systick_set_reload(8999); // 9000 pulses: 1kHz SysTick_init();
systick_interrupt_enable();
systick_counter_enable();
while(1){ while(1){
usbd_poll(usbd_dev); usbd_poll(usbd_dev);
@ -58,6 +64,9 @@ int main(){
} }
} }
/**
* SysTick interrupt: increment global time & send data buffer through USB
*/
void sys_tick_handler(){ void sys_tick_handler(){
usb_send_buffer(); usb_send_buffer();
Timer++; Timer++;

View File

@ -24,8 +24,16 @@
#ifndef __MAIN_H__ #ifndef __MAIN_H__
#define __MAIN_H__ #define __MAIN_H__
#include "cdcacm.h" #include <stdlib.h>
#include <string.h> // memcpy
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/usb/cdc.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/cm3/systick.h> #include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/rcc.h>
#include "user_proto.h" #include "user_proto.h"
#endif // __MAIN_H__ #endif // __MAIN_H__

252
with_opencm3/uart.c Normal file
View File

@ -0,0 +1,252 @@
/*
* uart.c - functions to work with UART
*
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "main.h"
#include "uart.h"
#include "cdcacm.h"
// Buffers for Tx
typedef struct {
uint8_t buf[UART_TX_DATA_SIZE];
uint8_t start; // index from where to start reading
uint8_t end; // index from where to start writing
} UART_buff;
static UART_buff TX_buffer[3]; // buffers for all three ports
void fill_uart_buff(uint32_t UART, uint8_t byte);
/**
* Set UART speed
* @param lc - UART parameters or NULL for value from cdcacm.c (started - B115200,8,N,1)
*/
void UART_setspeed(uint32_t UART, struct usb_cdc_line_coding *lc){
uint32_t tmp;
if(!lc) lc = &linecoding; // default linecoding from cdcacm.c
usart_set_baudrate(UART, lc->dwDTERate);
usart_set_databits(UART, lc->bDataBits);
switch(lc->bCharFormat){
case USB_CDC_1_5_STOP_BITS:
tmp = USART_STOPBITS_1_5;
break;
case USB_CDC_2_STOP_BITS:
tmp = USART_STOPBITS_2;
break;
case USB_CDC_1_STOP_BITS:
default:
tmp = USART_STOPBITS_1;
}
usart_set_stopbits(UART, tmp);
switch(lc->bParityType){
case USB_CDC_ODD_PARITY:
tmp = USART_PARITY_ODD;
break;
case USB_CDC_EVEN_PARITY:
tmp = USART_PARITY_EVEN;
break;
case USB_CDC_NO_PARITY:
default:
tmp = USART_PARITY_NONE;
}
usart_set_parity(UART, tmp);
usart_set_flow_control(UART, USART_FLOWCONTROL_NONE);
usart_set_mode(UART, USART_MODE_TX_RX);
}
/**
* Setup UART
*/
void UART_init(uint32_t UART){
uint32_t irq, rcc, rccgpio, gpioport, gpiopin;
switch(UART){
case USART2:
irq = NVIC_USART2_IRQ; // interrupt for given USART
rcc = RCC_USART2; // RCC timing of USART
rccgpio = RCC_GPIOA; // RCC timing of GPIO pin (for output)
TX_buffer[1].end = 0; // reset counters
TX_buffer[1].start = 0;
// output pin setup
gpioport = GPIO_BANK_USART2_TX;
gpiopin = GPIO_USART2_TX;
break;
case USART3:
irq = NVIC_USART3_IRQ;
rcc = RCC_USART3;
rccgpio = RCC_GPIOB;
TX_buffer[2].end = 0;
TX_buffer[2].start = 0;
gpioport = GPIO_BANK_USART3_TX;
gpiopin = GPIO_USART3_TX;
break;
case USART1:
default:
irq = NVIC_USART1_IRQ;
rcc = RCC_USART1;
rccgpio = RCC_GPIOA;
TX_buffer[0].end = 0;
TX_buffer[0].start = 0;
gpioport = GPIO_BANK_USART1_TX;
gpiopin = GPIO_USART1_TX;
}
// enable clocking
rcc_periph_clock_enable(RCC_AFIO); // alternate functions
rcc_periph_clock_enable(rcc); // USART
rcc_periph_clock_enable(rccgpio); // output pin
// enable output pin
gpio_set_mode(gpioport, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, gpiopin);
// enable IRQ
nvic_enable_irq(irq);
UART_setspeed(UART, NULL);
// Enable UART receive interrupt
USART_CR1(UART) |= USART_CR1_RXNEIE;
// Enable UART
usart_enable(UART);
}
/*
* UART interrupts
*/
// common
void UART_isr(uint32_t UART){
uint8_t bufidx = 0, data;
UART_buff *curbuff;
sendfun sf = uart1_send;
// Check if we were called because of RXNE
if(USART_SR(UART) & USART_SR_RXNE){
// parce incoming byte
data = usart_recv(UART);
switch(UART){
case USART1:
sf = uart1_send;
break;
case USART2:
sf = uart2_send;
break;
case USART3:
sf = uart3_send;
break;
default: // error - return
return;
}
parce_incoming_buf((char*)&data, 1, sf);
//fill_uart_buff(UART, data);
}
// Check if we were called because of TXE
if((USART_CR1(USART1) & USART_CR1_TXEIE) && (USART_SR(UART) & USART_SR_TXE)){
switch(UART){
case USART1:
bufidx = 0;
break;
case USART2:
bufidx = 1;
break;
case USART3:
bufidx = 2;
break;
default: // error - return
return;
}
curbuff = &TX_buffer[bufidx];
bufidx = curbuff->start; // start of data in buffer
if(bufidx != curbuff->end){ // there's data in buffer
// Put data into the transmit register
usart_send(UART, curbuff->buf[bufidx]);
if(++(curbuff->start) == UART_TX_DATA_SIZE) // bufidx > endidx && got end of buffer
curbuff->start = 0;
}else{ // Disable the TXE interrupt, it's no longer needed
USART_CR1(UART) &= ~USART_CR1_TXEIE;
// empty indexes
curbuff->start = 0;
curbuff->end = 0;
}
}
}
// particular
void usart1_isr(){
UART_isr(USART1);
}
void usart2_isr(){
UART_isr(USART2);
}
void usart3_isr(){
UART_isr(USART3);
}
// put data into buffer
void fill_uart_buff(uint32_t UART, uint8_t byte){
UART_buff *curbuff;
uint8_t bufidx = 0, endidx;
switch(UART){
case USART1:
bufidx = 0;
break;
case USART2:
bufidx = 1;
break;
case USART3:
bufidx = 2;
break;
default: // error - return
return;
}
curbuff = &TX_buffer[bufidx];
bufidx = curbuff->start; // start of data in buffer
endidx = curbuff->end; // end of data
curbuff->buf[endidx++] = byte; // put byte into buffer
// now check indexes
if(endidx != bufidx && endidx != UART_TX_DATA_SIZE){ // all OK - there's enough place for data
(curbuff->end)++; // just increment index in buffer
}else{ // dangerous situation: possible overflow
if(endidx == UART_TX_DATA_SIZE){ // end of buffer
if(bufidx != 0){ // no overflow
curbuff->end = 0;
goto end_of_fn;
}
}
// overflow: purge all data
USART_CR1(UART) &= ~USART_CR1_TXEIE; // disable TX interrupt - all will be done "by hands"
bufidx = curbuff->start; // refresh data index
for(endidx = bufidx; endidx < UART_TX_DATA_SIZE; endidx++) // first data porion
usart_send(UART, curbuff->buf[endidx]);
for(endidx = 0; endidx < bufidx; endidx++) // rest of data
usart_send(UART, curbuff->buf[endidx]);
curbuff->start = 0;
curbuff->end = 0;
return;
}
end_of_fn:
// enable interrupts to send data from buffer
USART_CR1(UART) |= USART_CR1_TXEIE;
}
/**
* send data over UART - one function for each uart
* @param byte - one byte to put in UART queue
*/
void uart1_send(uint8_t byte){
fill_uart_buff(USART1, byte);
}
void uart2_send(uint8_t byte){
fill_uart_buff(USART2, byte);
}
void uart3_send(uint8_t byte){
fill_uart_buff(USART3, byte);
}

37
with_opencm3/uart.h Normal file
View File

@ -0,0 +1,37 @@
/*
* uart.h
*
* Copyright 2014 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __UART_H__
#define __UART_H__
// Size of buffers
#define UART_TX_DATA_SIZE 64
void UART_init(uint32_t UART);
void UART_setspeed(uint32_t UART, struct usb_cdc_line_coding *linecoding);
void uart1_send(uint8_t byte);
void uart2_send(uint8_t byte);
void uart3_send(uint8_t byte);
#endif // __UART_H__

View File

@ -20,7 +20,8 @@
*/ */
#include "cdcacm.h" #include "cdcacm.h"
#include "user_proto.h" #include "main.h"
#include "uart.h"
// integer value given by user // integer value given by user
static volatile int32_t User_value = 0; static volatile int32_t User_value = 0;
@ -46,15 +47,18 @@ void parce_incoming_buf(char *buf, int len, sendfun s){
int i = 0; int i = 0;
if(Uval_ready == UVAL_START){ // we are in process of user's value reading if(Uval_ready == UVAL_START){ // we are in process of user's value reading
i += read_int(buf, len); i += read_int(buf, len);
}else{ }
if(Uval_ready == UVAL_ENTERED){ if(Uval_ready == UVAL_ENTERED){
print_int(User_value, s); // printout readed integer value for error control print_int(User_value, s); // printout readed integer value for error control
Uval_ready = UVAL_PRINTED; Uval_ready = UVAL_PRINTED;
} }
if(I && Uval_ready == UVAL_CHECKED) I(User_value, s); if(I && Uval_ready == UVAL_CHECKED){
Uval_ready = UVAL_BAD; // clear Uval_ready
I(User_value, s);
} }
for(; i < len; i++){ for(; i < len; i++){
command = buf[i]; command = buf[i];
if(!command) continue; // omit zero
switch (command){ switch (command){
case 'b': // turn LED off case 'b': // turn LED off
gpio_set(GPIOC, GPIO12); gpio_set(GPIOC, GPIO12);
@ -74,6 +78,20 @@ void parce_incoming_buf(char *buf, int len, sendfun s){
if(Uval_ready == UVAL_PRINTED) Uval_ready = UVAL_BAD; if(Uval_ready == UVAL_PRINTED) Uval_ready = UVAL_BAD;
else WRONG_COMMAND(); else WRONG_COMMAND();
break; break;
case 'u': // check USB connection
P("\r\nUSB ", s);
if(!USB_connected) P("dis", s);
P("connected\r\n",s);
break;
/*
case 'U': // test: init USART1
UART_init(USART1);
break;
*/
case '\n': // show newline as is
break;
case '\r':
break;
default: default:
WRONG_COMMAND(); // echo '?' on unknown command WRONG_COMMAND(); // echo '?' on unknown command
} }