2014/02/18

Nios II 시작해보자-4(SPI-Software)

NIOS II EDS 실행해서 프로젝트 만들고

아래와 같이 만들어봤다.
#include "sys/alt_stdio.h"
#include "system.h"
#include <stdlib .h>
#include "altera_avalon_spi_regs.h"

#define _NOP() asm ("nop")

unsigned char RxBuffer1[32];
unsigned char RxCounter1 = 0;

int UART1_get_Number(void){
unsigned char key_value, i;
int KeyIn;

do{
key_value = alt_getchar();
alt_putchar(key_value);
if(key_value == '\r'){
RxBuffer1[RxCounter1]='\0';
alt_putstr("\r\n");
KeyIn = atoi(RxBuffer1);

RxCounter1 = 0;
for(i = 0; i < 32; i++)
RxBuffer1[i] = 0;

return KeyIn;
}
else
RxBuffer1[RxCounter1++] = key_value;
}while((key_value != '\r')&&(RxCounter1 != 32));

}

int main()
{
int tmp, sum, avg, i, j, k, l;
alt_u16 write_data[1], read_data[1], adcs[15];
alt_u16 adc_ch, adc_cmd;

alt_putstr("20131118_1\r\n");
alt_putstr("1. HELP : Show menu explanation\r\n");
alt_putstr("2. Read ADS7961S\r\n");
alt_putstr("anonymous>");

/* Event loop never exits. */
while (1){
tmp = UART1_get_Number();

switch(tmp){
case 1:
alt_putstr("1. HELP : Show menu explanation\r\n");
alt_putstr("2. Read ADS7961S\r\n");
break;
case 2:
alt_putstr("2. Read ADS7961S\r\n");

//0010 01xx x000 0000
write_data[0] = 0x2400;
alt_avalon_spi_command(SPI_0_BASE, 0,
1, write_data,
0, read_data,
0);

for(i=0;i<16;i++){
alt_avalon_spi_command(SPI_0_BASE, 0,
0, write_data,
1, read_data,
0);
adcs[read_data[0]>>12] = read_data[0];
}

for(i=0;i<16;i++)
alt_printf("adc_ch %x : %x\r\n",i, (adcs[i]>>4) & 0xff);

break;
default :
alt_putstr("Invalid input value\r\n");
break;
}
alt_putstr("anonymous>");
}
return 0;
}



ADS7961S는 Manual, Auto-1, Auto-2 라는 세가지 채널 시퀀싱 모드를 제공한다.
난 Auto-1 모드를 선택해서 써볼란다.

write_data[0] =  0x2400;
alt_avalon_spi_command(SPI_0_BASE, 0,
1, write_data,
0, read_data,
0);
0x2400을 write해서 Auto-1 모드를 선택한다.

for(i=0;i<16;i++){
alt_avalon_spi_command(SPI_0_BASE, 0,
0, write_data,
1, read_data,
0);
adcs[read_data[0]>>12] = read_data[0];
}
16번 read해서 16채널의 adc 값을 읽어온다.

이게 다지 머



제대로 돌아가는지 함 돌려보자.

시발 좆같은 오실로를 쓰고있어서 파형 저장도 안돼고 나 썅.




























CH1 : MISO
CH2 : SCLK
CH3 : MOSI

인데 클럭말곤 안나오네. 0x2400 시그널이 왜 안나오지??

SPI read 부분은 빼고 0x1234를 write해서 시그널을 다시 확인해보자.
    //0010 01xx x000 0000
//write_data[0] = 0x2400;
write_data[0] = 0x1234;
alt_avalon_spi_command(SPI_0_BASE, 0,
1, write_data,
0, read_data,
0);
#if 0
for(i=0;i<16;i++){
alt_avalon_spi_command(SPI_0_BASE, 0,
0, write_data,
1, read_data,
0);
adcs[read_data[0]>>12] = read_data[0];
}

for(i=0;i<16;i++)
alt_printf("adc_ch %x : %x\r\n",i, (adcs[i]>>4) & 0xff);
#endif





























응??
0x0034??
앞에 12는 어디간겨??

int alt_avalon_spi_command(alt_u32 base, alt_u32 slave,
alt_u32 write_length, const alt_u8 * write_data,
alt_u32 read_length, alt_u8 * read_data,
alt_u32 flags)
Nios의 SPI 관련 함수의 선언부다...만 먼가 좀 이상하군 난 분명히 16bit width로 설정했는데 왜 write_data, read_data가 8bit지?? 하위 8bit만 write되고, 하위 8bit만 read되는 상황인디...

내가 머리가 나빠서 왜 저렇게 만든건진 모르것고 변수 선언부를 alt_u16으로 수정했다.

int alt_avalon_spi_command(alt_u32 base, alt_u32 slave,
alt_u32 write_length, const alt_u16 * write_data,//changed 16bit
alt_u32 read_length, alt_u16 * read_data, //changed 16bit
alt_u32 flags)
{
const alt_u16 * write_end = write_data + write_length; //changed 16bit
alt_u16 * read_end = read_data + read_length; //changed 16bit

alt_u32 write_zeros = read_length;
alt_u32 read_ignore = write_length;
alt_u32 status;

/* We must not send more than two bytes to the target before it has
* returned any as otherwise it will overflow. */
/* Unfortunately the hardware does not seem to work with credits > 1,
* leave it at 1 for now. */
alt_32 credits = 1;

/* Warning: this function is not currently safe if called in a multi-threaded
* environment, something above must perform locking to make it safe if more
* than one thread intends to use it.
*/

IOWR_ALTERA_AVALON_SPI_SLAVE_SEL(base, 1 << slave);

/* Set the SSO bit (force chipselect) only if the toggle flag is not set */
if ((flags & ALT_AVALON_SPI_COMMAND_TOGGLE_SS_N) == 0) {
IOWR_ALTERA_AVALON_SPI_CONTROL(base, ALTERA_AVALON_SPI_CONTROL_SSO_MSK);
}

/*
* Discard any stale data present in the RXDATA register, in case
* previous communication was interrupted and stale data was left
* behind.
*/
IORD_ALTERA_AVALON_SPI_RXDATA(base);

/* Keep clocking until all the data has been processed. */
for ( ; ; )
{

do
{
status = IORD_ALTERA_AVALON_SPI_STATUS(base);
}
while (((status & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) == 0 || credits == 0) &&
(status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) == 0);

if ((status & ALTERA_AVALON_SPI_STATUS_TRDY_MSK) != 0 && credits > 0)
{
credits--;

if (write_data < write_end)
IOWR_ALTERA_AVALON_SPI_TXDATA(base, *write_data++);
else if (write_zeros > 0)
{
write_zeros--;
IOWR_ALTERA_AVALON_SPI_TXDATA(base, 0);
}
else
credits = -1024;
};

if ((status & ALTERA_AVALON_SPI_STATUS_RRDY_MSK) != 0)
{
alt_u32 rxdata = IORD_ALTERA_AVALON_SPI_RXDATA(base);

if (read_ignore > 0)
read_ignore--;
else
*read_data++ = (alt_u16)rxdata; //changed 16bit
credits++;

if (read_ignore == 0 && read_data == read_end)
break;
}

}

/* Wait until the interface has finished transmitting */
do
{
status = IORD_ALTERA_AVALON_SPI_STATUS(base);
}
while ((status & ALTERA_AVALON_SPI_STATUS_TMT_MSK) == 0);

/* Clear SSO (release chipselect) unless the caller is going to
* keep using this chip
*/
if ((flags & ALT_AVALON_SPI_COMMAND_MERGE) == 0)
IOWR_ALTERA_AVALON_SPI_CONTROL(base, 0);

return read_length;
}



자 다시 돌려보자.





























좀 흔들렸네...어쨌든 0x1234 나왔다.

SPI write는 확인했고, 다음은 read를 확인해보자.
0x1234를 0x2400으로 바꾸고 주석처리한 부분도 해제해서 돌려보자.





























오케이 ADC값이 나온다.
10번 채널에 33이 출력된다. 16비트 데이터 중 상위 4비트는 채널값이고 그 다음 8비트가 ADC 값이다. 하위 4비트는 Don't care.















시리얼로 출력은 이렇게 나온다. 0x21 = 33























ADS7961S의 10번 채널에 센싱 저항을 연결해서 LED 듀티를 변경하면서 adc 값을 읽어봤다.

잘 되네.








2014/02/17

Nios II 시작해보자-4(SPI-Hardware)

기존 NIOS 코어에는 onchip_memory와 uart만 붙어있다.

여기에 SPI를 추가해서 써먹어 보자.

SPI 마스터는 NIOS, SPI 슬레이브는 ADS7961S ADC.

16채널, 8비트, 1MSPS ADC이고 당연히도 SPI 인터페이스다.


흠... 일단 qsys로 기존 시스템을 열어보자.

쪼기 SPI 보이네. 떠블 클릭


다음 메뉴들을 선택해서 SPI를 설정해야된다.

Type : Master/Slave
Num of select(SS_n) : [32:1]
SPI clock : 128000Hz
Width : [32:8]bits
Shift direction : MSB/LSB first
Clock polarity : 0/1
Clock phase : 0/1


난 이렇게 설정했다.

Type : Master                    <-- p="">Num of select(SS_n) : 1     <-- p="">
SPI clock : 128000Hz          <-- p="">
Width : 16bits                     <-- 16bit="" adc="" p="" spi="">
Shift direction : MSB first    <-- adc="" first="" msb="" p="" spi="">
Clock polarity : 0                <-- p="">
Clock phase : 0                  <-- p="">

클락 설정 설명 그림
이 중 맞는걸 쓰면 된다.

그리고 finish 눌러서 추가해주자.



System Contents 화면 상에서 clk, reset, spi_control_port 연결하고 Export 클릭해주고 IRQ도 연결해준다.
그다음 System 메뉴의 Assign Base Addresses와 Assign Interrupt Numbers를 클릭해주면 SPI 관련 설정은 끝난다.

그리고 Save As에서 nios_v003으로 저장한다. 기존 시스템은 건들지 말고 새로 저장해서 하자. 나중에 눈물 흘리지 말고

그다음 할 일은 Generation이지.

좀 시간이 걸린다만 에러없이 생성되면 아주 땡큐...난 에러 없이 생성되었다.


자...그럼 쿼터스에서 새로운 프로젝트를 만들어서 bdf 파일을 생성하고 방금 만든 nios_v003 블럭을 꽂아보자.

필요한거 다 연결하면 아마도 이런 모양이것지.

다음은 nios_v003의 qip 파일을 추가할 차례.
Assignments 탭의 Settings를 클릭해서 Files 카테고리로 가면 된다.
File name 항목의 ...을 클릭해서 nios_v003 폴더 내의 synthesis 폴더로 들어가면 nios_v003.qip 파일이 있다. 이거 선택하고 Add.

핀할당만 해주면 되것군.
Save 해주고 Synthesis 한 다음에 핀 할당 해줍시다.
저장한 다음 Start Compilation.

오케. 하드웨어는 만들었다. 다음은 프로그램 함 짜보자.